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

Tài liệu Các giao diện và mẫu phần 3 pdf

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 (178.96 KB, 9 trang )

// 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 {


get {

// Nếu TeamMemberEnumerator đứng trước phần tử đầu tiên
// hoặc sau phần tử cuối cùng thì ném ngoại lệ.
if (currentMember == -1 ||
currentMember > (sourceTeam.teamMembers.Count-1)) {

throw new InvalidOperationException();
}

// Nếu không, trả về TeamMember hiện tại.
return sourceTeam.teamMembers[currentMember];
}
}

// Hiện thực phương thức IEnumerator.MoveNext.
public bool MoveNext() {

// Nếu Team nằm dưới bất hợp lệ, ném ngoại lệ.
if (teamInvalid) {

throw new InvalidOperationException("Team modified");
}

// Nếu không, tiến đến TeamMember kế tiếp.
currentMember++;

// Trả về false nếu ta dịch qua khỏi TeamMember cuối cùng.
if (currentMember > (sourceTeam.teamMembers.Count-1)) {
return false;

} 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.

public delegate void TeamChangedEventHandler(Team t, EventArgs e);

// ArrayList dùng để chứa các đối tượng TeamMember.
private ArrayList teamMembers;

// Sự kiện dùng để báo cho TeamMemberEnumerator
// biết Team đã thay đổi.
public event TeamChangedEventHandler TeamChange;

// Phương thứ
c khởi dựng Team.
public Team() {

teamMembers = new ArrayList();
}

// Hiện thực phương thức IEnumerable.GetEnumerator.
public IEnumerator GetEnumerator() {

return new TeamMemberEnumerator(this);
}

// Thêm một đối tượng TeamMember vào Team.
public void AddMember(TeamMember member) {

teamMembers.Add(member);

if (TeamChange != null) {

TeamChange(this, null);

}
}
}
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) {

// Làm gì đó
}
• Lệnh foreach cũng hỗ trợ các kiểu có hiện thực một mẫu tương đương với mẫu
được định nghĩa bởi giao diện IEnumerable và IEnumerator, mặc dù kiểu đó
không hiện thực các giao diện này. Tuy nhiên, mã lệnh của bạn sẽ rõ ràng hơn
và dễ hiểu hơn nếu bạn hiện thực giao diện IEnumerable. Bạn hãy xem C#
Language Specification để biết chi tiết về các yêu cầu của lệnh foreach
[rosoft. com/net/ecma]
.
Ä Hiện
thực lớp khả-hủy (disposable class)
Í Bạn cần tạo một lớp có tham chiếu đến các tài nguyên không-được-quản-lý và
cung cấp một cơ chế để người dùng giải phóng các tài nguyên đó một cách tất
định.
▪ Hiện thực giao diện System.IDisposable, và giải phóng các tài nguyên không-
được-quản-lý khi mã client gọi phương thức IDisposable.Dispose.
Một đối tượng không được tham chiếu đến vẫn tồn tại trên vùng nhớ động (heap) và tiêu
thụ các tài nguyên cho đến khi bộ thu gom rác (Garbage Collector) giải phóng đối t
ượng
và các tài nguyên. Bộ thu gom rác sẽ tự động giải phóng các tài nguyên được-quản-lý
(như bộ nhớ), nhưng nó sẽ không giải phóng các tài nguyên không-được-quản-lý (như
file handle và kết nối cơ sở dữ liệu) được tham chiếu bởi các đối tượng được-quản-lý.
Nếu một đối tượng chứa các thành viên dữ liệu tham chiếu đến các tài nguyên không-
được-quản-lý, đối tượng này phải giải phóng các tài nguyên đó.
Một giải pháp là khai báo m
ột destructor—hay finalizer—cho lớp. Trước khi giải phóng
phần bộ nhớ do một thể hiện của lớp sử dụng, bộ thu gom rác sẽ gọi finalizer của đối
tượng này. Finalizer có thể thực hiện các bước cần thiết để giải phóng các tài nguyên
không-được-quản-lý. Vì bộ thu gom rác chỉ sử dụng một tiểu trình để thực thi tất cả các
finalizer, việc sử dụng finalizer có thể bất lợi trong quá trình thu gom rác và ảnh hưởng

đế
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à

nhiệm vụ của mã client, mặc dù bạn có thể hiện thực sự đồng bộ bên trong phương
thức Dispose.
3. Phương thức Dispose không nên ném ngoại lệ.
4. Vì phương thức Dispose dọn dẹp tất cả nên không cần gọi finalizer của đối tượng.
Phương thức Dispose của bạn nên gọi phương thức GC.SuppressFinalize để bảo
đảm finalizer không được gọi trong quá trình thu gom rác.
5. Hiện thực một finalizer sao cho phương thức Dispose sẽ được nó gọi theo một cơ
chế an toàn trong trường hợp mã client gọi Dispose không đúng. Tuy nhiên, nên
tránh tham chiếu đến các đối tượng được-quản-lý trong finalizer vì không rõ trạng
thái của đối tượng.
6. Nếu một lớp khả-hủy thừa kế một lớp khả-hủy khác, phương thức Dispose của lớp
con phải gọi phương thức Dispose của lớp cha. Gói phần mã của lớp con trong một
khối try và gọi phương thức Dispose của lớp cha trong một mệnh đề finally để bảo
đảm việc thực thi.
7. Các phương thức và thuộc tính khác của lớp nên ném ngoại lệ
System.ObjectDisposedException nếu mã client thực thi một phương thức trên một
đối tượng đã bị hủy.
Lớp DisposeExample dưới đây minh họa một hiện thực phổ biến của mẫu Dispose:
using System;

// Hiện thực giao diện IDisposable.
public class DisposeExample : IDisposable {

// Phần tử dữ liệu private dùng để báo hiệu
// đối tượng đã bị hủy hay chưa.
bool isDisposed = false;

// Phần tử dữ liệu private dùng để lưu giữ
// handle của tài nguyên không-được-quản-lý.
private IntPtr resourceHandle;


// 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

// 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
// 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
}
}
}

×