
Chương 16 : Các giao diện và mẫu thông dụng
Chương này trình bày cách hiện thực các mẫu (pattern) sẽ được sử dụng thường xuyên
trong quá trình phát triển các ứng dụng Microsoft .NET Framework. Một số mẫu được
chuẩn hóa bằng các giao diện được định nghĩa trong thư viện lớp .NET Framework. Một
số khác thì ít cứng nhắc hơn, nhưng vẫn yêu cầu bạn thực hiện các cách tiếp cận cụ thể để
thiết kế và hiện thực các kiểu của bạn. Các mục trong chương này mô tả cách:
Tạo các kiểu khả-tuần-tự-hóa để bạn có thể dễ dàng lưu trữ vào đĩa, gửi qua mạng,
hoặc truyển bằng trị qua các biên miền ứng dụng (mục 16.1).
Cung cấp một cơ chế dùng để tạo bản sao đầy đủ và chính xác của đối tượng (mục
16.2).
Hiện thực các kiểu sao cho dễ dàng so sánh và sắp xếp (mục 16.3).
Hỗ trợ việc liệt kê các phần tử trong các tập hợp tùy biến (mục 16.4).
Bảo đảm rằng một kiểu có sử dụng các tài nguyên không-được-quản-lý sẽ giải
phóng các tài nguyên đó khi không còn cần đến chúng nữa (mục 16.5).
Hiển thị dạng chuỗi của các đối tượng biến đổi dựa trên format specifier (mục 16.6).
Hiện thực các kiểu đối số sự kiện và ngoại lệ tùy biến (bạn sẽ thường xuyên sử dụng
chúng trong quá trình phát triển ứng dụng) (mục 16.7 và 16.8).
Hiện thực các mẫu thiết kế thông dụng Singleton và Observer bằng các tính năng có
sẵn của C# và thư viện lớp .NET Framework (mục 16.9 và 16.10).
1.1 Hiện thực kiểu khả-tuần-tự-hóa (serializable type)
V
V
Bạn cần hiện thực một kiểu tùy biến khả-tuần-tự-hóa, cho phép bạn:
• Lưu trữ các thể hiện của kiểu vào kho lưu trữ (file hay cơ sở dữ liệu).
• Chuyển các thể hiện của kiểu qua mạng.
• Truyền các thể hiện của kiểu “bằng trị” qua các biên miền ứng dụng.
#
#
Đối với việc tuần tự hóa các kiểu đơn giản, hãy áp dụng đặc tính
System.SerializableAttribute vào khai báo kiểu. Đối với các kiểu phức tạp hơn,
hoặc để kiểm soát nội dung và cấu trúc của dữ liệu được-tuần-tự-hóa, hãy hiện
thực giao diện System.Runtime.Serialization.ISerializable.
Mục 2.12 đã trình bày cách tuần tự hóa và giải tuần tự hóa một đối tượng bằng các lớp
formatter (được cấp cùng với thư viện lớp .NET Framework). Tuy nhiên, theo mặc định
thì các kiểu không là khả-tuần-tự-hóa. Để hiện thực một kiểu tùy biến là khả-tuần-tự-hóa,
bạn phải áp dụng đặc tính SerializableAttribute vào khai báo kiểu. Khi tất cả các trường
dữ liệu trong kiểu đều là khả-tuần-tự-hóa, việc áp dụng SerializableAttribute là tất cả

những gì cần làm để khiến cho kiểu tùy biến của bạn là khả-tuần-tự-hóa. Nếu bạn hiện
thực một lớp tùy biến dẫn xuất từ một lớp cơ sở, lớp cơ sở cũng phải là khả-tuần-tự-hóa.
Mỗi lớp formatter chứa logic cần thiết để tuần tự hóa các kiểu được gắn với đặc tính
SerializableAttribute và sẽ tuần tự hóa tất cả các trường public, protected, và private.
Đoạn mã dưới đây trình bày các khai báo kiểu và khai báo trường của một lớp khả-tuần-
tự-hóa có tên là Employee.
using System;
[Serializable]
public class Employee {
private string name;
private int age;
private string address;
§
}
# Các lớp dẫn xuất từ một kiểu khả-tuần-tự-hóa không thừa kế đặc tính
SerializableAttribute. Để khiến cho các kiểu dẫn xuất là khả-tuần-tự-hóa, bạn
phải khai báo chúng là khả-tuần-tự-hóa bằng cách áp dụng đặc tính
SerializableAttribute.
Bạn có thể ngăn việc tuần tự hóa một trường nào đó bằng cách áp dụng đặc tính
System.NonSerializedAttribute cho trường này. Bạn nên ngăn việc tuần tự hóa đối với
các trường sau:
• Chứa các kiểu dữ liệu không-khả-tuần-tự-hóa.
• Chứa các giá trị có thể không hợp lệ khi đối tượng được giải tuần tự hóa, ví dụ: kết
nối cơ sở dữ liệu, địa chỉ bộ nhớ, ID của tiểu trình, và handle của tài nguyên không-
được-quản-lý.
• Chứa các thông tin nhạy cảm hay riêng tư, ví dụ: mật khẩu, khóa mật hóa, và các chi
tiết riêng về người hay tổ chức.
• Chứa các dữ liệu dễ dàng tái tạo hay thu lấy được từ các nguồn khác—đặc biệt khi
dữ liệu lớn.
Nếu ngăn việc tuần tự hóa một số trường, bạn phải hiện thực kiểu sao cho bù lại việc
những dữ liệu nào đó sẽ không hiện diện khi một đối tượng được giải tuần tự hóa. Đáng
tiếc, bạn không thể tạo hay thu lấy các trường dữ liệu bị mất trong một phương thức khởi
dựng vì formatter không gọi phương thức khởi dựng trong quá trình giải tuần tự hóa đối
tượng. Giải pháp thông thường nhất là hiện thực mẫu “Lazy Initialization”, trong đó kiểu
của bạn sẽ tạo hay thu lấy dữ liệu ngay lần đầu tiên cần đến.

Đoạn mã dưới đây trình bày một phiên bản đã được chỉnh sửa của lớp Employee với đặc
tính NonSerializedAttribute được áp dụng cho trường address, nghĩa là formatter sẽ
không tuần tự hóa giá trị của trường này. Lớp Employee hiện thực các thuộc tính công
khai dùng để truy xuất các thành viên dữ liệu riêng, là nơi thuận tiện để hiện thực “Lazy
Initialization” cho trường address.
using System;
[Serializable]
public class Employee {
private string name;
private int age;
[NonSerialized]
private string address;
// Phương thức khởi dựng đơn giản.
public Employee(string name, int age, string address) {
this.name = name;
this.age = age;
this.address = address;
}
// Thuộc tính công khai dùng để truy xuất tên của nhân viên.
public string Name {
get { return name; }
set { name = value; }
}
// Thuộc tính công khai dùng để truy xuất tuổi của nhân viên.
public int Age {
get { return age; }
set { age = value; }
}
// Thuộc tính công khai dùng để truy xuất địa chỉ của nhân viên.
// Sử dụng "Lazy Initialization" để thiết lập địa chỉ vì
// đối tượng được-giải-tuần-tự-hóa sẽ không có giá trị địa chỉ.

public string Address {
get {
if (address == null) {
// Nạp địa chỉ từ kho lưu trữ.
}
return address;
}
set {
address = value;
}
}
}
Đối với phần lớn các kiểu tùy biến, việc sử dụng đặc tính SerializableAttribute và
NonSerializedAttribute sẽ đáp ứng đủ nhu cầu tuần tự hóa của bạn. Nếu cần kiểm soát
quá trình tuần tự hóa, bạn cần hiện thực giao diện ISerializable. Các lớp formatter sử
dụng logic khác nhau khi tuần tự hóa và giải tuần tự hóa thể hiện của các kiểu có hiện
thực ISerializable. Để hiện thực đúng ISerializable, bạn phải:
• Khai báo rằng kiểu của bạn có hiện thực giao diện ISerializable.
• Áp dụng đặc tính SerializableAttribute vào khai báo kiểu như vừa được mô tả;
không sử dụng NonSerializedAttribute vì nó sẽ không có tác dụng.
• Hiện thực phương thức ISerializable.GetObjectData (được sử dụng trong quá trình
tuần tự hóa), phương thức này nhận các kiểu đối số sau:
▪ System.Runtime.Serialization.SerializationInfo
▪ System.Runtime.Serialization.StreamingContext
• Hiện thực một phương thức khởi dựng không công khai (được sử dụng trong quá
trình giải tuần tự hóa), phương thức này nhận cùng đối số như phương thức
GetObjectData. Nhớ rằng, nếu bạn có ý định dẫn xuất một số lớp từ lớp khả-tuần-tự-
hóa thì phương thức khởi dựng này phải là protected.
• Nếu bạn tạo một lớp khả-tuần-tự-hóa từ một lớp cơ s
ở cũng có hiện thực
ISerializable, thì phương thức GetObjectData và phương thức khởi dựng (dùng để
giải tuần tự hóa) của bạn phải gọi các phương thức tương đương trong lớp cha.
Trong quá trình tuần tự hóa, formatter sẽ gọi phương thức GetObjectData và truyền cho
nó các tham chiếu SerializationInfo và StreamingContext.
• Bạn phải đổ dữ liệu cần tuần tự hóa vào đối tượng SerializationInfo. Lớp
SerializationInfo cung cấp phương thức AddValue dùng để thêm dữ liệu. Với mỗi
lần gọi AddValue, bạn phải chỉ định tên dữ liệu (tên này sẽ được sử dụng trong quá

trình giải tuần tự hóa để thu lấy dữ liệu). Phương thức AddValue có đến 16 phiên
bản nạp chồng, cho phép bạn thêm nhiều kiểu dữ liệu khác nhau vào đối tượng
SerializationInfo.
• Đối tượng StreamingContext cung cấp các thông tin về chủ định và đích của dữ liệu
được-tuần-tự-hóa, cho phép bạn chọn tuần tự hóa dữ liệu nào. Ví dụ, bạn có thể cần
tuần tự hóa dữ liệu riêng nếu nó được dành cho một miền ứng dụng khác trong cùng
tiến trình, nhưng không cần nếu dữ liệu sẽ được ghi ra file.
Trong quá trình giải tuần tự hóa, formatter sẽ gọi phương thức khởi dựng việc giải tuần
tự hóa, lại truyền cho nó các tham chiếu SerializationInfo và StreamingContext.
• Kiểu của bạn phải trích dữ liệu đã-được-tuần-tự-hóa từ đối tượng SerializationInfo
bằng một trong các phương thức SerializationInfo.Get*, ví dụ: GetString, GetInt32,
hay GetBoolean.
• Đối tượng StreamingContext cung cấp các thông tin về nguồn gốc của dữ liệu đã-
được-tuần-tự-hóa, phản ánh logic mà bạn đã hiện thực cho việc tuần tự hóa.
# Trong quá trình tuần tự hóa chuẩn, formatter không sử dụng khả năng của đối
tượng StreamingContext để cho biết các chi tiết về nguồn gốc, đích, và chủ
định của dữ liệu được-tuần-tự-hóa. Tuy nhiên, nếu muốn thực hiện quá trình
tuần tự hóa tùy biến, bạn có thể cấu hình đối tượng StreamingContext của
formatter trước khi bắt đầu quá trình tuần tự hóa và giải tuần tự hóa. Tham
khảo tài liệu .NET Framework SDK để có thêm thông tin về lớp
StreamingContext.
Ví dụ dưới đây trình bày phiên bản đã được chỉnh sửa của lớp Employee, có hiện thực
giao diện ISerializable. Trong phiên bản này, lớp Employee không tuần tự hóa trường
address nếu đối tượng StreamingContext chỉ định rằng đích của dữ liệu được-tuần-tự-hóa
là file. Phương thức Main sẽ giải thích việc tuần tự hóa và giải tuần tự hóa của một đối
tượng Employee.
using System;
using System.Runtime.Serialization;
[Serializable]
public class Employee : ISerializable {
private string name;
private int age;
private string address;
// Phương thức khởi dựng đơn giản.