
Ä 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:
1. 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.
2. Đặ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.
3. Đá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ó.
4. Hiện thực thêm các thuộc tính và các thành viên dữ liệu để hỗ trợ trạng thái sự kiện
mà bạn cần truyền cho các phương thức thụ lý sự kiện. Tốt nhất là làm cho trạng
thái sự kiện trở nên bất biến (immutable), như vậy bạn nên sử dụng các thành viên
dữ liệu private readonly và sử dụng các thuộc tính public để cho phép truy xuất chỉ-
đọc đến các thành viên dữ liệu này.
5. Hiện thực một phương thức khởi dựng public hỗ trợ cấu hình ban đầu của trạng thái
sự kiện.
6. Làm cho lớp đối số sự kiện của bạn trở nên khả-tuần-tự-hóa (serializable) để 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 đối
số sự kiện. Tuy nhiên, nếu lớp đối số sự kiện có các yêu cầu tuần tự hóa đặc biệt,
bạn phải hiện thực giao diện System.Runtime.Serialization.ISerializable (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).
Đoạn mã dưới đây trình bày một lớp đối số sự kiện tùy biến có tên là
MailReceivedEventArgs. Giả sử có một mail-server truyền các thể hiện của lớp
MailReceivedEventArgs cho các phương thức thụ lý sự kiện nhận một thông điệp e-mail.
Lớp này chứa các thông tin về người gửi và chủ đề của thông điệp e-mail.
using System;
[Serializable]

public sealed class MailReceivedEventArgs : EventArgs {
// Các thành viên private readonly giữ trạng thái sự kiện
// (được phân bổ cho tất cả các phương thức thụ lý sự kiện).
// Lớp MailReceivedEventArgs sẽ cho biết ai đã gửi mail
// và chủ đề là gì.
private readonly string from;
private readonly string subject;
// Phương thức khởi dựng (khởi tạo trạng thái sự kiện).
public MailReceivedEventArgs(string from, string subject) {
this.from = from;
this.subject = subject;
}
// Các thuộc tính chỉ-đọc cho phép truy xuất
// trạng thái sự kiện.
public string From { get { return from; } }
public string Subject { get { return subject; } }
}
Ä Hiện
thực mẫu Singleton
Í Bạn cần bảo đảm chỉ có một thể hiện của một kiểu tồn tại ở một thời điểm cho
trước và thể hiện đó là khả-truy-xuất đối với tất cả các phần tử của ứng dụng.
▪ Hiện thực kiểu này theo mẫu Singleton như sau:
7. Hiện thực một thành viên tĩnh private để giữ một tham chiếu đến thể hiện
của kiểu.
8. Hiện thực một thuộc tính tĩnh khả-truy-xuất-công-khai để cho phép truy
xuất chỉ-đọc đến thể hiện.
9. Hiện thực một phương thức khởi dựng private để mã lệnh không thể tạo
thêm các thể hiện của kiểu.
Trong tất cả các mẫu được biết đến, có lẽ mẫu Singleton được biết đến nhiều nhất và
thường được sử dụng nhất. Mục đích của mẫu Singleton là bảo đảm chỉ có một thể hiện
của một kiểu tồn tại ở một thời điểm cho trước và cho phép truy xuất toàn cục đến các
chức năng của thể hiện đó. Đoạn mã dưới đây trình bày một hiện thực của mẫu Singleton
cho một lớp có tên là SingletonExample:

public class SingletonExample {
// Thành viên tĩnh dùng để giữ một tham chiếu đến thể hiện singleton.
private static SingletonExample instance;
// Phương thức khởi dựng tĩnh dùng để tạo thể hiện singleton.
// Một cách khác là sử dụng "Lazy Initialization" trong
// thuộc tính Instance.
static SingletonExample () {
instance = new SingletonExample();
}
// Phương thức khởi dựng private dùng để ngăn mã lệnh tạo thêm
// các thể hiện của kiểu singleton.
private SingletonExample () {}
// Thuộc tính public cho phép truy xuất đến thể hiện singleton.
public static SingletonExample Instance {
get { return instance; }
}
// Các phương thức public cung cấp chức năng của singleton.
public void SomeMethod1 () { /*..*/ }
public void SomeMethod2 () { /*..*/ }
}
Để gọi các chức năng của lớp SingletonExample, bạn có thể lấy về một tham chiếu đến
singleton bằng thuộc tính Instance và rồi gọi các phương thức của nó. Bạn cũng có thể
trực tiếp thực thi các thành viên của singleton thông qua thuộc tính Instance. Đoạn mã
dưới đây trình bày cả hai cách này:
// Thu lấy tham chiếu đến singleton và gọi một phương thức của nó.
SingletonExample s = SingletonExample.Instance;
s.SomeMethod1();
// Thực thi chức năng của singleton mà không cần tham chiếu.
SingletonExample.Instance.SomeMethod2();
Ä Hiện
thực mẫu Observer

Í Bạn cần hiện thực một cơ chế hiệu quả để một đối tượng (subject) báo với các
đối tượng khác (observer) về những thay đổi trạng thái của nó.
▪ Hiện thực mẫu Observer bằng các kiểu ủy nhiệm (đóng vai trò là các con trỏ
hàm an-toàn-về-kiểu-dữ-liệu—type-safe function pointer) và các kiểu sự kiện.
Cách truyền thống khi hiện thực mẫu Observer là hiện thực hai giao diện: một để mô tả
observer (IObserver) và một để mô tả subject (ISubject). Các đối tượng có hiện thực
IObserver sẽ đăng ký với subject, cho biết chúng muốn được thông báo về các sự kiện
quan trọng (như thay đổi trạng thái) tác động đến subject. Subject chịu trách nhiệm quản
lý danh sách các observer đã đăng ký và thông báo với chúng khi đáp ứng các sự kiện tác
động đến subject. Subject thường thông báo với observer bằng phương thức Notify (được
khai báo trong giao diện IObserver). Subject có thể truyền dữ liệu cho observer trong
phương thức Notify, hoặc observer có thể cần gọi một phương thức được khai báo trong
giao diện ISubject để thu lấy thêm các chi tiết về sự kiện.
Mặc dù bạn có thể hiện thực mẫu Observer bằng C# theo cách vừa được mô tả, nhưng vì
mẫu Observer quá phổ biến trong các giải pháp phần mềm hiện đại nên C# và .NET
Framework cung cấp các kiểu sự kiện và ủy nhiệm để đơn giản hóa hiện thực của nó. Sử
dụng sự kiện và ủy nhiệm nghĩa là bạn không cần khai báo giao diện IObserver và
ISubject. Ngoài ra, bạn không cần hiện thực các logic cần thiết để quản lý và thông báo
với các observer đã đăng ký—đây chính là phần dễ xảy ra lỗi nhất khi viết mã.
.NET Framework sử dụng một hiện thực cho mẫu Observer dựa-trên-sự-kiện và dựa-trên-
ủy-nhiệm thường xuyên đến nỗi nó được đặt một cái tên: mẫu Event. File
ObserverExample.cs chứa một hiện thực cho mẫu Event. Ví dụ này bao gồm:
10. Lớp Thermostat (subject)—theo dõi nhiệt độ hiện tại và thông báo với observer khi
có sự thay đổi nhiệt độ.
11. Lớp TemperatureChangeEventArgs—là một hiện thực tùy biến của lớp
System.EventArgs, được sử dụng để đóng gói dữ liệu cần phân bổ cho observer.
12. Ủy nhiệm TemperatureEventHandler—định nghĩa chữ ký của phương thức mà tất
cả các observer của đối tượng Thermostat phải hiện thực, và đối tượng Thermostat
sẽ gọi phương thức này trong sự kiện thay đổi nhiệt độ.
13. Lớp TemperatureChangeObserver và TemperatureAverageObserver—là các
observer của lớp Thermostat.
Lớp TemperatureChangeEventArgs (được trình bày bên dưới) dẫn xuất từ lớp
System.EventArgs. Lớp này sẽ chứa tất cả các dữ liệu cần thiết để subject truyền cho các
observer khi nó thông báo với chúng về một sự kiện. Nếu không cần truyền dữ liệu, bạn
không phải định nghĩa một lớp đối số mới (bạn chỉ cần truyền đối số null khi dựng nên sự
kiện). Xem mục 16.8 để biết rõ hơn về cách hiện thực lớp đối số sự kiện tùy biến.
// Lớp đối số sự kiện chứa thông tin về sự kiện thay đổi nhiệt độ.

// Một thể hiện của lớp này sẽ được truyền cùng với mỗi sự kiện.
public class TemperatureChangeEventArgs : System.EventArgs {
// Các thành viên dữ liệu chứa nhiệt độ cũ và mới.
private readonly int oldTemperature, newTemperature;
// Phương thức khởi dựng (nhận giá trị nhiệt độ cũ và mới).
public TemperatureChangeEventArgs(int oldTemp, int newTemp) {
oldTemperature = oldTemp;
newTemperature = newTemp;
}
// Các thuộc tính dùng để truy xuất các giá trị nhiệt độ.
public int OldTemperature { get { return oldTemperature; } }
public int NewTemperature { get { return newTemperature; } }
}
Đoạn mã dưới đây trình bày cách khai báo ủy nhiệm TemperatureEventHandler. Dựa trên
khai báo này, tất cả các observer phải hiện thực một phương thức (tên không quan trọng),
trả về void và nhận hai đối số: một đối tượng Thermostat và một đối tượng
TemperatureChangeEventArgs. Trong quá trình thông báo, đối số Thermostat chỉ đến đối
tượng Thermostat đã dựng nên sự kiện, và đối số TemperatureChangeEventArgs chứa
các dữ liệu về nhiệt độ cũ và mới.
// Ủy nhiệm cho biết chữ ký mà tất cả các phương thức
// thụ lý sự kiện phải hiện thực.
public delegate void TemperatureEventHandler(Thermostat s,
TemperatureChangeEventArgs e);
Lớp Thermostat là đối tượng bị giám sát trong mẫu Observer (Event) này. Theo lý thuyết,
một thiết bị giám sát thiết lập nhiệt độ hiện tại bằng cách gọi thuộc tính Temperature trên
một đối tượng Thermostat. Khi đó, đối tượng Thermostat dựng nên sự kiện
TemperatureChange và gửi một đối tượng TemperatureChangeEventArgs đến mỗi
observer. Dưới đây là mã lệnh cho lớp Thermostat:
// Lớp mô tả một bộ ổn nhiệt, là nguồn gốc của các sự kiện thay đổi
// nhiệt độ. Trong mẫu Observer, đối tượng Thermostat là subject.
public class Thermostat {
// Trường private dùng để giữ nhiệt độ hiện tại.
private int temperature = 0;