Các giao diện và mẫu phần 2

Chia sẻ: Nghia Bui Tuan | Ngày: | Loại File: PDF | Số trang:8

0
55
lượt xem
12
download

Các giao diện và mẫu phần 2

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Bạn cần một cơ chế dùng để so sánh các kiểu tùy biến, cho phép bạn dễ dàng sắp xếp tập hợp chứa các thể hiện của kiểu này. Để cung cấp một cơ chế so sánh chuẩn cho một kiểu, hiện thực giao diện System.IComparable.

Chủ đề:
Lưu

Nội dung Text: Các giao diện và mẫu phần 2

  1. 1.1 Hiện thực kiểu khả-so-sánh (comparable type) Bạn cần một cơ chế dùng để so sánh các kiểu tùy biến, cho phép bạn dễ dàng sắp xếp tập hợp chứa các thể hiện của kiểu này. Để cung cấp một cơ chế so sánh chuẩn cho một kiểu, hiện thực giao diện System.IComparable. Để hỗ trợ nhiều dạng so sánh, tạo riêng từng kiểu trợ giúp (helper) và các kiểu này hiện thực giao diện System.Collections.IComparer. Nếu muốn sắp xếp kiểu của bạn chỉ theo một thứ tự nào đó (như ID tăng dần, hay tên theo thứ tự alphabet), bạn nên hiện thực giao diện IComparable. Giao diện này định nghĩa phương thức CompareTo như sau: int CompareTo(object obj); Đối tượng (obj) được truyền cho phương thức phải cùng kiểu với đối tượng đang gọi, nếu không CompareTo sẽ ném ngoại lệ System.ArgumentException. Giá trị do CompareTo trả về được tính như sau: • Nếu đối tượng hiện tại nhỏ hơn obj, trả về một số âm (chẳng hạn, -1). • Nếu đối tượng hiện tại có cùng giá trị như obj, trả về zero. • Nếu đối tượng hiện tại lớn hơn obj, trả về một số dương (chẳng hạn, 1). Phép so sánh này thực hiện điều gì là tùy thuộc vào kiểu đã hiện thực giao diện IComparable. Ví dụ, nếu muốn sắp xếp dựa theo tên, bạn cần thực hiện phép so sánh chuỗi (String). Tuy nhiên, nếu muốn sắp xếp dựa theo ngày sinh, bạn cần thực hiện phép so sánh ngày (System.DateTime). Để hỗ trợ nhiều dạng sắp xếp cho một kiểu cụ thể, bạn phải hiện thực riêng rẽ từng kiểu trợ giúp và các kiểu này hiện thực giao diện IComparer. Giao diện này định nghĩa phương thức Compare như sau: int Compare(object x, object y); Kiểu trợ giúp phải đóng gói logic cần thiết để so sánh hai đối tượng và trả về một giá trị dựa trên logic như sau: • Nếu x nhỏ hơn y, trả về một số âm (chẳng hạn, -1). • Nếu x có cùng giá trị như y, trả về zero. • Nếu x lớn hơn y, trả về một số dương (chẳng hạn, 1). Lớp Newspaper dưới đây hiện thực cả giao diện IComparable và IComparer. Phương thức Newspaper.CompareTo thực hiện phép so sánh không phân biệt chữ hoa-thường hai đối tượng Newspaper dựa trên trường name của chúng. Một lớp private lồng bên trong có tên là AscendingCirculationComparer hiện thực IComparer và so sánh hai đối tượng Newspaper dựa trên trường circulation của chúng. Đối tượng
  2. AscendingCirculationComparer được thu lấy bằng thuộc tính tĩnh Newspaper.CirculationSorter. using System; using System.Collections; public class Newspaper : IComparable { private string name; private int circulation; private class AscendingCirculationComparer : IComparer { int IComparer.Compare(object x, object y) { // Xử lý các tham chiếu null. // Null được coi như nhỏ hơn bất cứ giá trị nào khác. if (x == null && y == null) return 0; else if (x == null) return -1; else if (y == null) return 1; // Trường hợp x và y tham chiếu đến cùng một đối tượng. if (x == y) return 0; // Bảo đảm x và y đều là các thể hiện của Newspaper. Newspaper newspaperX = x as Newspaper; if (newspaperX == null) { throw new ArgumentException("Invalid object type", "x"); } Newspaper newspaperY = y as Newspaper; if (newspaperY == null) { throw new ArgumentException("Invalid object type", "y"); } // So sánh circulation. IComparer quy định rằng: // trả về một số âm nếu x < y // trả về zero nếu x = y
  3. // trả về một số dương nếu x > y // Dễ dàng hiện thực logic này bằng phép tính số nguyên. return newspaperX.circulation - newspaperY.circulation; } } public Newspaper(string name, int circulation) { this.name = name; this.circulation = circulation; } // Khai báo một thuộc tính chỉ-đọc, trả về một thể hiện của // AscendingCirculationComparer. public static IComparer CirculationSorter{ get { return new AscendingCirculationComparer(); } } public override string ToString() { return string.Format("{0}: Circulation = {1}", name, circulation); } // Phương thức CompareTo so sánh hai đối tượng Newspaper dựa trên // phép so sánh trường name (không phân biệt chữ hoa-thường). public int CompareTo(object obj) { // Một đối tượng luôn được coi như lớn hơn null. if (obj == null) return 1; // Trường hợp đối tượng kia là một tham chiếu đến đối tượng này. if (obj == this) return 0; // Ép đối tượng kia về Newspaper. Newspaper other = obj as Newspaper; // Nếu "other" là null, nó không phải là một thể hiện của // Newspaper. Trong trường hợp này, CompareTo phải ném
  4. // ngoại lệ System.ArgumentException. if (other == null) { throw new ArgumentException("Invalid object type", "obj"); } else { // 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); }
  5. Console.WriteLine(Environment.NewLine); Console.WriteLine("Newspaper list sorted by name (default order):"); newspapers.Sort(); foreach (Newspaper n in newspapers) { Console.WriteLine(n); } 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 1.2 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.
  6. 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 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 Mô tả viên 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 Current 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. 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ề MoveNext 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.
  7. 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ữ Reset 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. 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:
Đồng bộ tài khoản