Tải bản đầy đủ (.pdf) (10 trang)

Các giải pháp lập trình CSharp- P75 potx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (2.62 MB, 10 trang )

631
Chương 16: Các giao diện và mẫu thông dụng
// resourceHandle =
}
// Destructor/Finalizer.
~DisposeExample() {
// Gọi phiên bản nạp chồng protected của Dispose
// và truyền giá trị "false" để cho biết rằng
// Dispose đang được gọi trong quá trình thu gom rác,
// chứ không phải bởi mã consumer.
Dispose(false);
}
// Hiện thực public của phương thức IDisposable.Dispose, được gọi
// bởi consumer của đối tượng để giải phóng các tài nguyên không-
// được-quản-lý một cách tất định.
public void Dispose() {
// Gọi phiên bản nạp chồng protected của Dispose và truyền
// giá trị "true" để cho biết rằng Dispose đang được gọi
// bởi mã consumer, chứ không phải bởi bộ thu gom rác.
Dispose(true);
// Vì phương thức Dispose thực hiện tất cả việc dọn dẹp cần
// thiết nên bảo đảm bộ thu gom rác không gọi destructor của lớp.
GC.SuppressFinalize(this);
}
// Phiên bản nạp chồng protected của phương thức Dispose. Đối số
// disposing cho biết phương thức được gọi bởi mã consumer (true),
// hay bởi bộ thu gom rác (false).
protected virtual void Dispose(bool disposing) {
if (!isDisposed) {
if (disposing) {
// Phương thức này được gọi bởi mã consumer. Gọi


632
Chương 16: Các giao diện và mẫu thông dụng
// phương thức Dispose của các thành viên dữ liệu
// được-quản-lý có hiện thực giao diện IDisposable.
// §
}
// Giải phóng tất cả các tài nguyên không-được-quản-lý
// và thiết lập giá trị của các thành viên dữ liệu
// được-quản-lý thành null.
// Close(resourceHandle);
}
// Báo rằng đối tượng này đã bị hủy.
isDisposed = true;
}
// Trước khi thực thi bất kỳ chức năng nào, bảo đảm rằng
// Dispose chưa được thực thi trên đối tượng.
public void SomeMethod() {
// Ném một ngoại lệ nếu đối tượng đã bị hủy.
if (isDisposed) {
throw new ObjectDisposedException("DisposeExample");
}
// Thực thi chức năng của phương thức
// §
}

public static void Main() {
// Lệnh using bảo đảm phương thức Dispose được gọi
// cả khi ngoại lệ xảy ra.
using (DisposeExample d = new DisposeExample()) {
// Làm gì đó với d

633
Chương 16: Các giao diện và mẫu thông dụng
}
}
}
6.
6.
Hi n th c ki u kh -đ nh-d ng (formattable type)ệ ự ể ả ị ạ
Hi n th c ki u kh -đ nh-d ng (formattable type)ệ ự ể ả ị ạ


Bạn cần hiện thực một kiểu có thể được sử dụng theo các
format string
, và có thể
tạo ra những biểu diễn chuỗi khác nhau cho nội dung của nó dựa vào
format
specifier
.


Hiện thực giao diện
System.IFormattable
.
Đoạn mã dưới đây minh họa cách sử dụng format specifier (phần in đậm) trong phương thức
WriteLine
của lớp
System.Console
.
double a = 345678.5678;
uint b = 12000;

byte c = 254;
Console.WriteLine("a = {0}, b = {1}, and c = {2}", a, b, c);
Console.WriteLine("a = {0:c0}, b = {1:n4}, and c = {2,10:x5}", a, b, c);
Khi chạy trên máy với thiết lập bản địa là English (U.K.), đoạn mã này sẽ cho kết xuất như
sau (thay đổi nội dung của format specifier sẽ thay đổi định dạng của kết xuất một cách đáng
kể mặc dù dữ liệu vẫn không thay đổi):
a = 345678.5678, b = 12000, and c = 254
a = £345,679, b = 12,000.0000, and c = 000fe
Để kích hoạt việc hỗ trợ format specifier, bạn phải hiện thực giao diện
IFormattable
. Giao
diện này khai báo một phương thức có tên là
ToString
với chữ ký như sau:
string ToString(string format, IFormatProvider formatProvider);
Đối số
format
là một
System.String
chứa format string (chuỗi định dạng). Format string là
phần format specifier phía sau dấu hai chấm. Ví dụ, trong format specifier
{2,10:x5}
(ở ví dụ
trên), "
x5
" là format string. Format string chứa những chỉ thị mà thể hiện
IFormattable
sẽ sử
dụng khi tạo ra dạng chuỗi cho nội dung của nó. Tài liệu .NET Framework phát biểu rằng:
những kiểu có hiện thực

IFormattable
thì phải hỗ trợ format string "
G
" (general), nhưng
những format string được hỗ trợ khác thì phụ thuộc vào hiện thực. Đối số
format

null
nếu
format specifier không chứa phần format string, ví dụ
{0}
hay
{1,20}
.
Đối số
formatProvider
là tham chiếu đến một thể hiện
System.IFormatProvider
(dùng để truy
xuất các thông tin bản địa—bao gồm các dữ liệu như biểu tượng tiền tệ hay số lượng chữ số
thập phân). Theo mặc định,
formatProvider

null
, nghĩa là bạn sẽ sử dụng các thiết lập bản
địa của tiểu trình hiện hành (có thể lấy được thông qua phương thức tĩnh
CurrentCulture
của
lớp
System.Globalization.CultureInfo

).
.NET Framework chủ yếu sử dụng
IFormattable
để hỗ trợ việc định dạng các kiểu giá trị,
nhưng nó có thể được sử dụng cho bất kỳ kiểu nào. Ví dụ, lớp
Person
dưới đây có hiện thực
giao diện
IFormattable
. Lớp này chứa danh hiệu và tên của một người, và sẽ trả về tên theo
634
Chương 16: Các giao diện và mẫu thông dụng
các định dạng khác nhau tùy vào format string. Lớp
Person
không sử dụng các thiết lập bản
địa do đối số
formatProvider
cung cấp.
using System;
public class Person : IFormattable {
// Các thành viên private dùng để lưu trữ danh hiệu
// và tên của một người.
private string title;
private string[] names;
// Phương thức khởi dựng dùng để thiết lập danh hiệu và tên.
public Person(string title, params string[] names) {
this.title = title;
this.names = names;
}
// Chép đè phương thức Object.ToString để trả về

// tên theo định dạng general.
public override string ToString() {
return ToString("G", null);
}
// Hiện thực phương thức IFormattable.ToString để trả về
// tên theo các dạng khác nhau dựa trên format string.
public string ToString(string format,
IFormatProvider formatProvider) {
string result = null;
// Sử dụng định dạng general nếu format = null.
if (format == null) format = "G";
// Nội dung của format string cho biết định dạng của tên.
switch (format.ToUpper()[0]) {
635
Chương 16: Các giao diện và mẫu thông dụng
case 'S':
// Sử dụng dạng short: first-initial và surname.
result = names[0][0] + ". " + names[names.Length-1];
break;
case 'P':
// Sử dụng dạng polite: title, initials, và surname.
if (title != null && title.Length != 0) {
result = title + ". ";
}
for (int count = 0; count < names.Length; count++) {
if ( count != (names.Length - 1)) {
result += names[count][0] + ". ";
} else {
result += names[count];
}

}
break;
case 'I':
// Sử dụng dạng informal: chỉ có first-name.
result = names[0];
break;
case 'G':
default:
// Sử dụng dạng mặc định/general: first-name và surname.
result = names[0] + " " + names[names.Length-1];
break;
}
return result;
}
}
Đoạn mã dưới đây trình bày cách sử dụng khả năng định dạng của lớp
Person
:
// Tạo một đối tượng Person mô tả một người có tên là
// Mr. Richard Glen David Peters.
Person person =
636
Chương 16: Các giao diện và mẫu thông dụng
new Person("Mr", "Richard", "Glen", "David", "Peters");
// Hiển thị tên bằng nhiều format string khác nhau.
System.Console.WriteLine("Dear {0:G},", person);
System.Console.WriteLine("Dear {0:P},", person);
System.Console.WriteLine("Dear {0:I},", person);
System.Console.WriteLine("Dear {0},", person);
System.Console.WriteLine("Dear {0:S},", person);

Khi được thực thi, đoạn mã này sinh ra kết xuất như sau:
Dear Richard Peters,
Dear Mr. R. G. D. Peters,
Dear Richard,
Dear Richard Peters,
Dear R. Peters,
7.
7.
Hi n th c l p ngo i l tùy bi nệ ự ớ ạ ệ ế
Hi n th c l p ngo i l tùy bi nệ ự ớ ạ ệ ế


Bạn cần tạo một lớp ngoại lệ tùy biến sao cho bạn có thể sử dụng cơ chế thụ lý
ngoại lệ của bộ thực thi để thụ lý các ngoại lệ đặc-trưng-ứng-dụng.


Tạo một lớp khả-tuần-tự-hóa, thừa kế lớp
System.ApplicationException
và hiện
thực các phương thức khởi dựng với chữ ký như sau:
public CustomException() : base() {}
public CustomException(string message) : base(message) {}
public CustomException(string message, Exception inner)
: base(message, inner) {}
Thêm bất cứ thành viên dữ liệu tùy biến nào mà ngoại lệ cần đến, bao gồm các phương thức
khởi dựng và các thuộc tính cần thiết để thao tác các thành viên dữ liệu.
Các lớp ngoại lệ là duy nhất, bạn không được khai báo các lớp mới để hiện thực chức năng
mới hay mở rộng. Cơ chế thụ lý ngoại lệ của bộ thực thi (được trưng ra bởi các lệnh:
try
,

catch
, và
finally
) làm việc dựa trên kiểu ngoại lệ bị ném, chứ không phải các thành viên
chức năng hay dữ liệu được hiện thực bởi ngoại lệ bị ném.
Nếu cần ném một ngoại lệ, bạn nên sử dụng một lớp ngoại lệ có sẵn trong thư viện lớp .NET
Framework (nếu tồn tại một lớp phù hợp). Dưới đây là một số ngoại lệ hữu ích:

System.ArgumentNullException
—khi mã lệnh truyền một giá trị đối số
null
cho một
phương thức không hỗ trợ đối số
null
.

System.ArgumentOutOfRangeException
—khi mã lệnh truyền cho phương thức một giá
trị đối số không phù hợp (lớn quá hay nhỏ quá).
637
Chương 16: Các giao diện và mẫu thông dụng

System.FormatException
—khi mã lệnh truyền cho phương thức một đối số
String
chứa
dữ liệu không được định dạng đúng.
Nếu không có lớp ngoại lệ nào đáp ứng được nhu cầu của bạn, hoặc bạn cảm thấy ứng dụng
của bạn sẽ được lợi từ việc sử dụng các ngoại lệ đặc-trưng-ứng-dụng, bạn có thể tạo một lớp
ngoại lệ cho mình. Để tích hợp ngoại lệ tùy biến với cơ chế thụ lý ngoại lệ của bộ thực thi và

vẫn giữ tính nhất quán với mẫu được hiện thực bởi các lớp ngoại lệ có sẵn, bạn cần:

Đặt một tên có ý nghĩa cho lớp ngoại lệ tùy biến, kết thúc bằng từ
Exception
, chẳng
hạn,
TypeMismatchException
hay
RecordNotFoundException
.

Thừa kế lớp
ApplicationException
. Về cơ bản, lớp ngoại lệ tùy biến phải thừa kế lớp
System.Exception
, nếu không trình biên dịch sẽ dựng lên lỗi khi bạn ném ngoại lệ.
ApplicationException
thừa kế
Exception
và được đề nghị làm lớp sơ sở cho tất cả các
lớp ngoại lệ đặc-trưng-ứng-dụng.

Đánh dấu lớp ngoại lệ tùy biến là
sealed
nếu bạn không muốn các lớp ngoại lệ khác có
thể thừa kế nó.

Hiện thực thêm các thuộc tính và các thành viên dữ liệu để hỗ trợ các thông tin tùy biến
mà lớp ngoại lệ này cung cấp.


Hiện thực ba phương thức khởi dựng
public
với chữ ký như dưới đây và bảo đảm
chúng gọi phương thức khởi dựng của lớp cơ sở:
public CustomException() : base() {}
public CustomException(string message): base(message) {}
public CustomException(string message, Exception inner)
: base(message, inner) {}

Làm cho lớp ngoại tùy biến trở nên khả-tuần-tự-hóa để bộ thực thi có thể marshal các
thể hiện của nó qua các biên miền ứng dụng và biên máy. Áp dụng đặc tính
System.SerializableAttribute
thường là đã đủ cho các lớp ngoại lệ không hiện thực
các thành viên dữ liệu tùy biến. Tuy nhiên, vì
Exception
hiện thực giao diện
System.Runtime.Serialization.ISerializable
nên nếu ngoại lệ của bạn có khai báo
các thành viên dữ liệu tùy biến, bạn phải chép đè phương thức
ISerializable.GetObjectData
của lớp
Exception
cũng như hiện thực một phương thức
khởi dựng giải tuần tự hóa với chữ ký như dưới đây. Nếu lớp ngoại lệ của bạn là
sealed
, đánh dấu phương thức khởi dựng giải tuần tự hóa là
private
; nếu không thì
đánh dấu nó là
protected

.
private CustomException(SerializationInfo info,
StreamingContext context) {}
Phương thức
GetObjectData
và phương thức khởi dựng giải tuần tự hóa phải gọi
phương thức tương đương trong lớp cơ sở để cho phép lớp cơ sở thực hiện tuần tự hóa
và giải tuần tự hóa dữ liệu của nó một cách đúng đắn (xem mục 16.1 để biết cách làm
cho một lớp trở nên khả-tuần-tự-hóa).
Dưới đây là một lớp ngoại lệ tùy biến có tên là
CustomException
(thừa kế lớp
ApplicationException
). Lớp này khai báo hai thành viên dữ liệu tùy biến: một chuỗi có tên là
stringInfo
và một giá trị luận lý có tên là
booleanInfo
.
638
Chương 16: Các giao diện và mẫu thông dụng
using System;
using System.Runtime.Serialization;
// Đánh dấu CustomException là Serializable (khả-tuần-tự-hóa).
[Serializable]
public sealed class CustomException : ApplicationException {
// Các thành viên dữ liệu tùy biến cho CustomException.
private string stringInfo;
private bool booleanInfo;
// Ba phương thức khởi dựng chuẩn; chỉ cần gọi phương thức
// khởi dựng của lớp cơ sở (System.ApplicationException).

public CustomException() : base() {}
public CustomException(string message): base(message) {}
public CustomException(string message, Exception inner)
: base(message, inner) {}
// Phương thức khởi dựng giải tuần tự hóa (cần cho giao diện
// ISerialization). Vì CustomException là sealed nên phương thức
// khởi dựng này là private. Nếu CustomException không phải là
// sealed thì phương thức khởi dựng này nên được khai báo là
// protected để các lớp dẫn xuất có thể gọi nó trong quá trình
// giải tuần tự hóa.
private CustomException(SerializationInfo info,
StreamingContext context) : base (info, context) {
// Giải tuần tự hóa mỗi thành viên dữ liệu tùy biến.
stringInfo = info.GetString("StringInfo");
booleanInfo = info.GetBoolean("BooleanInfo");
}
// Các phương thức khởi dựng cho phép mã lệnh thiết lập
639
Chương 16: Các giao diện và mẫu thông dụng
// các thành viên dữ liệu tùy biến.
public CustomException(string message, string stringInfo,
bool booleanInfo): this(message) {
this.stringInfo = stringInfo;
this.booleanInfo = booleanInfo;
}
public CustomException(string message, Exception inner,
string stringInfo, bool booleanInfo) : this(message, inner) {
this.stringInfo = stringInfo;
this.booleanInfo = booleanInfo;
}

// Các thuộc tính chỉ-đọc cho phép truy xuất đến các
// thành viên dữ liệu tùy biến.
public string StringInfo {
get { return stringInfo; }
}
public bool BooleanInfo {
get { return booleanInfo; }
}
// Phương thức GetObjectData (được khai báo trong giao diện
// ISerializable) được sử dụng trong quá trình tuần tự hóa
// CustomException. Vì CustomException có khai báo các thành
// viên dữ liệu tùy biến nên nó phải chép đè hiện thực
// GetObjectData của lớp cơ sở.
public override void GetObjectData(SerializationInfo info,
StreamingContext context) {
// Tuần tự hóa các thành viên dữ liệu tùy biến.
info.AddValue("StringInfo", stringInfo);
info.AddValue("BooleanInfo", booleanInfo);
// Gọi lớp cơ sở để tuần tự hóa các thành viên của nó.
base.GetObjectData(info, context);
}
640
Chương 16: Các giao diện và mẫu thông dụng
// Chép đè thuộc tính Message của lớp cơ sở (để kèm các
// thành viên dữ liệu tùy biến vào).
public override string Message {
get {
string message = base.Message;
if (stringInfo != null) {
message += Environment.NewLine +

stringInfo + " = " + booleanInfo;
}
return message;
}
}
}
Trong các ứng dụng lớn, bạn sẽ thường xuyên hiện thực một vài lớp ngoại lệ tùy biến. Bạn
cần lưu tâm đến cách tổ chức các ngoại lệ tùy biến và mã lệnh sẽ sử dụng chúng như thế nào.
Nói chung, tránh tạo ra các lớp ngoại lệ mới trừ khi mã lệnh cần nỗ lực bắt ngoại lệ đó; sử
dụng các thành viên dữ liệu để thu thông tin, chứ không phải các lớp ngoại lệ. Ngoài ra, tránh
phân cấp lớp theo chiều sâu mà nên phân cấp cạn, theo chiều rộng.
8.
8.
Hi n th c đ i s s ki n tùy bi nệ ự ố ố ự ệ ế
Hi n th c đ i s s ki n tùy bi nệ ự ố ố ự ệ ế


Khi dựng lên một sự kiện, bạn cần truyền một trạng thái đặc-trưng-sự-kiện cho
các phương thức thụ lý sự kiện.


Tạo một lớp đối số sự kiện tùy biến dẫn xuất từ lớp
System.EventArg
. Khi dựng
lên sự kiện, hãy tạo một thể hiện của lớp đối số sự kiện và truyền nó cho các
phương thức thụ lý sự kiện.
Khi khai báo các kiểu sự kiện, thông thường bạn sẽ cần truyền trạng thái đặc-trưng-sự-kiện
cho các phương thức thụ lý sự kiện. Để tạo một lớp đối số sự kiện tùy biến tuân theo mẫu
Event do .NET Framework định nghĩa, bạn cần:


Dẫn xuất lớp đối số sự kiện tùy biến từ lớp
EventArgs
. Lớp
EventArgs
không chứa dữ
liệu và được sử dụng cùng với các sự kiện không cần truyền trạng thái.

Đặt một tên có ý nghĩa cho lớp đối số sự kiện tùy biến, kết thúc bằng từ
EventArgs
;
chẳng hạn,
DiskFullEventArgs
hay
MailReceivedEventArgs
.

Đánh dấu lớp đối số sự kiện là
sealed
nếu bạn không muốn các lớp đối số sự kiện khác
có thể thừa kế nó.

×