// Lớp Team mô tả tập hợp các đối tượng TeamMember. Hiện thực giao diện
// IEnumerable để hỗ trợ việc liệt kê các đối tượng TeamMember.
public class Team : IEnumerable {
// TeamMemberEnumerator là một lớp private lồng bên trong, cung cấp
// chức năng liệt kê các đối tượng TeamMember trong tập hợp
// Team. Vì là lớp lồng bên trong nên TeamMemberEnumerator
// có thể truy xuất các thành viên private của lớp Team.
private class TeamMemberEnumerator : IEnumerator {
private Team sourceTeam;
// Giá trị luận lý cho biết Team nằm dưới có thay đổi hay không.
private bool teamInvalid = false;
// Giá trị nguyên cho biết TeamMember hiện tại (chỉ số
// trong ArrayList). Giá tr
ị ban đầu là -1.
private int currentMember = -1;
// Phương thức khởi dựng (nhận một tham chiếu đến Team).
internal TeamMemberEnumerator(Team team) {
this.sourceTeam = team;
sourceTeam.TeamChange +=
new TeamChangedEventHandler(this.TeamChange);
}
// Hiện thực thuộc tính IEnumerator.Current.
public object Current {
} else {
return true;
}
}
// Hiện thực phương thức IEnumerator.Reset. Phương thức này
// reset vị trí của TeamMemberEnumerator về đầu tập hợp Team.
public void Reset() {
// Nếu Team nằm dưới bất hợ
p lệ, ném ngoại lệ.
if (teamInvalid) {
throw new InvalidOperationException("Team modified");
}
// Dịch con trỏ currentMember về trước phần tử đầu tiên.
currentMember = -1;
}
// Phương thức thụ lý sự kiện tập hợp Team nằm dưới thay đổi.
internal void TeamChange(Team t, EventArgs e) {
// Báo hiệu Team nằm dưới hiện đang bất hợp lệ.
teamInvalid = true;
}
}
// Ủy nhiệm dùng để chỉ định chữ ký mà tất cả
// các phương thức thụ lý sự kiện phải hiện thực.
}
}
}
Nếu lớp tập hợp của bạn chứa nhiều kiểu dữ liệu khác nhau và bạn muốn liệt kê chúng
một cách riêng rẽ, việc hiện thực giao diện IEnumerable trên lớp tập hợp này thì vẫn còn
thiếu. Trong trường hợp này, bạn cần hiện thực một số thuộc tính trả về các thể hiện khác
nhau của IEnumerator. Ví dụ, nếu lớp Team mô tả cả các thành viên và các máy tính
trong đội, bạn có thể hiện th
ực các thuộc tính này như sau:
// Thuộc tính dùng để liệt kê các thành viên trong đội.
public IEnumerator Members {
get {
return new TeamMemberEnumerator(this);
}
}
// Thuộc tính dùng để liệt kê các computer trong đội.
public IEnumerator Computers {
get {
return new TeamComputerEnumerator(this);
}
}
Khi đó, bạn có thể sử dụng các enumerator này như sau:
Team team = new Team();
§
foreach(TeamMember in team.Members) {
// Làm gì đó
}
foreach(TeamComputer in team.Computers) {
đế
n hiệu năng của ứng dụng. Ngoài ra, bạn không thể kiểm soát khi bộ thực thi giải
phóng các tài nguyên không-được-quản-lý vì bạn không thể trực tiếp gọi finalizer của
một đối tượng, và bạn chỉ có quyền kiểm soát hạn chế trên các hoạt động của bộ thu gom
rác bằng lớp System.GC.
Bằng cách sử dụng finalizer, .NET Framework định nghĩa mẫu Dispose như một phương
tiện cung cấp quyề
n kiểm soát khi bộ thực thi giải phóng các tài nguyên không-được-
quản-lý. Để hiện thực mẫu Dispose, lớp phải hiện thực giao diện IDisposable. Giao diện
này khai báo một phương thức có tên là Dispose; trong đó, bạn phải hiện thực phần mã
cần thiết để giải phóng các tài nguyên không-được-quản-lý.
Các thể hiện của các lớp có hiện thực mẫu Dispose được gọi là các đối tượng khả-hủy
(disposable object). Khi mã lệ
nh đã hoàn tất với một đối tượng khả-hủy, nó sẽ gọi
phương thức Dispose của đối tượng để giải phóng các tài nguyên không-được-quản-lý,
vẫn dựa vào bộ thu gom rác để giải phóng các tài nguyên được-quản-lý của đối tượng.
Cần hiểu rằng bộ thực thi không bắt buộc hủy các đối tượng; việc gọi phương thức
Dispose là nhiệm vụ của client. Tuy nhiên, vì thư viện l
ớp .NET Framework sử dụng mẫu
Dispose rộng khắp nên C# cung cấp lệnh using để đơn giản hóa việc sử dụng các đối
tượng khả-hủy. Đoạn mã sau trình bày cấu trúc của lệnh using:
using (FileStream fileStream = new FileStream("SomeFile.txt",
FileMode.Open)) {
// Làm gì đó với đối tượng fileStream
}
Dưới đây là một số điểm cần lưu ý khi hiện thực mẫu Dispose:
1. Mã client nên có khả năng gọi đi gọi lại phương thức Dispose mà không gây ra các
ảnh hưởng bất lợi.
2. Trong các ứng dụng hỗ-trợ-đa-tiểu-trình, điều quan trọng là chỉ có một tiểu trình
thực thi phương thức Dispose. Thông thường, bảo đảm sự đồng bộ tiểu trình là
// Phương thức khởi dựng.
public DisposeExample() {
// Thu lấy tham chiếu đến tài nguyên không-được-quản-lý.
// 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
// 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
}
}
}