1. Trang chủ
  2. » Công Nghệ Thông Tin

Các giải pháp lập trình CSharp- P74 ppt

10 129 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 2,62 MB

Nội dung

621 Chương 16: Các giao diện và mẫu thông dụng // Tính giá trị trả về bằng cách thực hiện phép so sánh // trường name (không phân biệt chữ hoa-thường). // Vì name là chuỗi nên cách dễ nhất là dựa vào khả năng // so sánh của lớp String (thực hiện phép so sánh chuỗi // có phân biệt bản địa). return string.Compare(this.name, other.name, true); } } } Phương thức Main minh họa phép so sánh và khả năng sắp xếp nhờ có hiện thực giao diện IComparable và IComparer . Phương thức này sẽ tạo một tập hợp System.Collections.ArrayList chứa năm đối tượng Newspaper , sau đó sắp xếp ArrayList hai lần bằng phương thức ArrayList.Sort . Lần đầu, thao tác Sort sử dụng cơ chế so sánh mặc định của Newspaper (thông qua phương thức IComparable.CompareTo ). Lần sau, thao tác Sort sử dụng đối tượng AscendingCirculationComparer (thông qua phương thức IComparer.Compare ). public static void Main() { ArrayList newspapers = new ArrayList(); newspapers.Add(new Newspaper("Tuoi Tre", 125780)); newspapers.Add(new Newspaper("Echip", 55230)); newspapers.Add(new Newspaper("Thanh Nien", 235950)); newspapers.Add(new Newspaper("Phu Nu", 88760)); newspapers.Add(new Newspaper("Tiep Thi", 5670)); Console.WriteLine("Unsorted newspaper list:"); foreach (Newspaper n in newspapers) { Console.WriteLine(n); } Console.WriteLine(Environment.NewLine); Console.WriteLine("Newspaper list sorted by name (default order):"); newspapers.Sort(); foreach (Newspaper n in newspapers) { Console.WriteLine(n); } 622 Chương 16: Các giao diện và mẫu thông dụng Console.WriteLine(Environment.NewLine); Console.WriteLine("Newspaper list sorted by circulation:"); newspapers.Sort(Newspaper.CirculationSorter); foreach (Newspaper n in newspapers) { Console.WriteLine(n); } } Chạy phương thức Main sẽ sinh ra kết quả như sau: Unsorted newspaper list: Tuoi Tre: Circulation = 125780 Echip: Circulation = 55230 Thanh Nien: Circulation = 235950 Phu Nu: Circulation = 88760 Tiep Thi: Circulation = 5670 Newspaper list sorted by name (default order): Echip: Circulation = 55230 Phu Nu: Circulation = 88760 Thanh Nien: Circulation = 235950 Tiep Thi: Circulation = 5670 Tuoi Tre: Circulation = 125780 Newspaper list sorted by circulation: Tiep Thi: Circulation = 5670 Echip: Circulation = 55230 Phu Nu: Circulation = 88760 Tuoi Tre: Circulation = 125780 Thanh Nien: Circulation = 235950 4. 4. Hi n th c ki u kh -li t-kê (enumerable type)ệ ự ể ả ệ Hi n th c ki u kh -li t-kê (enumerable type)ệ ự ể ả ệ   Bạn cần tạo một kiểu tập hợp sao cho nội dung của nó có thể được liệt kê bằng lệnh foreach .   Hiện thực giao diện System.IEnumerable trong kiểu tập hợp của bạn. Phương thức GetEnumerator của giao diện IEnumerable trả về một enumerator—một đối 623 Chương 16: Các giao diện và mẫu thông dụng tượng có hiện thực giao diện System.IEnumerator . Giao diện IEnumerator định nghĩa các phương thức sẽ được lệnh foreach sử dụng để kiệt kê tập hợp. Một bộ chỉ mục bằng số (numerical indexer) cho phép bạn duyệt qua các phần tử của một tập hợp bằng vòng lặp for . Tuy nhiên, kỹ thuật này không cung cấp mức trừu tượng phù hợp với các cấu trúc dữ liệu phi tuyến, như cây và tập hợp đa chiều. Lệnh foreach cung cấp một cơ chế duyệt qua các đối tượng của một tập hợp mà không quan tâm cấu trúc bên trong của chúng là gì. Để hỗ trợ ngữ nghĩa foreach , đối tượng chứa tập hợp phải hiện thực giao diện System.IEnumerable . Giao diện này khai báo một phương thức có tên là GetEnumerator , phương thức này không nhận đối số và trả về một đối tượng System.IEnumerator : IEnumerator GetEnumerator(); Đối tượng IEnumerator là đối tượng hỗ trợ việc liệt kê các phần tử của tập hợp. Giao diện IEnumerator cung cấp một con chạy chỉ-đọc, chỉ-tiến (read-only, forward-only cursor) dùng để truy xuất các thành viên của tập hợp nằm dưới. Bảng 16.1 mô tả các thành viên của giao diện IEnumerator . Bảng 16.1 Các thành viên của giao diện IEnumerator Thành viên Mô tả Current Thuộc tính này trả về phần tử dữ liệu hiện tại. Khi enumerator được tạo ra, Current chỉ đến vị trí đứng trước phần tử dữ liệu đầu tiên, nghĩa là bạn phải gọi MoveNext trước khi sử dụng Current . Nếu Current được gọi và enumerator đang đứng trước phần tử đầu tiên hoặc sau phần tử cuối cùng trong tập hợp dữ liệu, Current sẽ ném ngoại lệ System.InvalidOperationException . MoveNext Phương thức này dịch chuyển enumerator sang phần tử dữ liệu kế tiếp trong tập hợp; trả về true nếu còn phần tử, trả về false nếu không còn phần tử. Nếu nguồn dữ liệu nằm dưới thay đổi trong thời gian sống của enumerator, MoveNext sẽ ném ngoại lệ InvalidOperationException . Reset Phương thức này dịch chuyển enumerator về vị trí đứng trước phần tử đầu tiên trong tập hợp dữ liệu. Nếu nguồn dữ liệu nằm dưới thay đổi trong thời gian sống của enumerator, Reset sẽ ném ngoại lệ InvalidOperationException . [ Các lớp TeamMember , Team , và TeamMemberEnumerator minh họa việc hiện thực giao diện IEnumerable và IEnumerator . Lớp TeamMember mô tả một thành viên của một đội: // Lớp TeamMember mô tả một thành viên trong đội. public class TeamMember { public string Name; public string Title; // Phương thức khởi dựng đơn giản. 624 Chương 16: Các giao diện và mẫu thông dụng public TeamMember(string name, string title) { Name = name; Title = title; } // Trả về chuỗi mô tả TeamMember. public override string ToString() { return string.Format("{0} ({1})", Name, Title); } } Lớp Team (mô tả một đội) là một tập hợp các đối tượng TeamMember . Lớp này hiện thực giao diện IEnumerable và khai báo một lớp có tên là TeamMemberEnumerator để cung cấp chức năng liệt kê. Thông thường, các lớp tập hợp sẽ trực tiếp hiện thực cả giao diện IEnumerable và IEnumerator . Tuy nhiên, sử dụng một lớp enumerator riêng biệt là cách đơn giản nhất để cho phép nhiều enumerator—và nhiều tiểu trình—liệt kê đồng thời các phần tử của Team . Team hiện thực mẫu Observer bằng cách sử dụng các thành viên sự kiện và ủy nhiệm để báo cho tất cả các đối tượng TeamMemberEnumerator biết Team nằm dưới có thay đổi hay không (xem mục 16.10 để có thêm thông tin về mẫu Observer). Lớp TeamMemberEnumerator là một lớp private lồng bên trong nên bạn không thể tạo các thể hiện của nó, trừ khi thông qua phương thức Team.GetEnumerator . Dưới đây là phần mã cho lớp Team và TeamMemberEnumerator : // 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; 625 Chương 16: Các giao diện và mẫu thông dụng // 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) { 626 Chương 16: Các giao diện và mẫu thông dụng 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; } } 627 Chương 16: Các giao diện và mẫu thông dụng // Ủ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 628 Chương 16: Các giao diện và mẫu thông dụng 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 [ http://msdn.microsoft. com/net/ecma ] . 629 Chương 16: Các giao diện và mẫu thông dụng 5. 5. Hi n th c l p kh -h y (disposable class)ệ ự ớ ả ủ 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: 630 Chương 16: Các giao diện và mẫu thông dụng • 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. • 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 . • Phương thức Dispose không nên ném ngoại lệ. • 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. • 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. • 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. • 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ý. . 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. 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. độ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ý

Ngày đăng: 08/07/2014, 17:20

TỪ KHÓA LIÊN QUAN