24/05/2018, 20:28

Cơ chế ủy quyền

Trong thời kỳ đầu của máy tính, chương trình được thực hiện theo trình tự xử lý từng bước tuần tự cho đến khi hoàn thành, và nếu người dùng thực hiện một sự tương tác thì sẽ làm hạn chế sự điều khiển hoạt động khác của chương trình cho đến khi sự tương tác ...

Trong thời kỳ đầu của máy tính, chương trình được thực hiện theo trình tự xử lý từng bước tuần tự cho đến khi hoàn thành, và nếu người dùng thực hiện một sự tương tác thì sẽ làm hạn chế sự điều khiển hoạt động khác của chương trình cho đến khi sự tương tác với người dùng chấm dứt.

Tuy nhiên, ngày nay với mô hình lập trình giao diện người dùng đồ họa (GUI: Graphical User Interface) đòi hỏi một cách tiếp cận khác, và được biết như là lập trình điều khiển sự kiện (event-driven programming). Chương trình hiện đại này đưa ra một giao diện tương tác với người dùng và sau đó thì chờ cho người sử dụng kích hoạt một hành động nào đó. Người sử dụng có thể thực hiện nhiều hành động khác nhau như: chọn các mục chọn trong menu, nhấn một nút lệnh, cập nhật các ô chứa văn bản,...Mỗi hành động như vậy sẽ dẫn đến một sự kiện (event) được sinh ra. Một số các sự kiện khác cũng có thể được xuất hiện mà không cần hành động trực tiếp của người dùng. Các sự kiện này xuất hiện do các thiết bị như đồng hồ của máy tính phát ra theo chu kỳ thời gian, thư điện tử được nhận, hay đơn giản là báo một hành động sao chép tập tin hoàn thành,...

Một sự kiện được đóng gói như một ý tưởng “chuyện gì đó xảy ra” và chương trình phải đáp ứng lại với sự kiện đó. Cơ chế sự kiện và ủy quyền gắn liền với nhau, bởi vì khi một sự kiện xuất hiện thì cần phải phân phát sự kiện đến trình xử lý sự kiện tương ứng. Thông trường một trình xử lý sự kiện được thực thi trong C# như là một sự ủy quyền.

Ủ y quyền cho phép một lớp có thể yêu cầu một lớp khác làm một công việc nào đó, và khi thực hiện công việc đó thì phải báo cho lớp biết. Ủy quyền cũng co thể được sử dụng để xác nhận những phương thức chỉ được biết lúc thực thi chương trình, và chúng ta sẽ tìm hiểu kỹ vấn đề này trong phần chính của chương.

Trong ngôn ngữ C#, ủy quyền là lớp đối tượng đầu tiên (first-class object), được hỗ trợ đầy đủ bởi ngôn ngữ lập trình. Theo kỹ thuật thì ủy quyền là kiểu dữ liệu tham chiếu được dùng để đóng gói một phương thức với tham số và kiểu trả về xác định. Chúng ta có thể đóng gói bất cứ phương thức thích hợp nào vào trong một đối tượng ủy quyền. Trong ngôn ngữ C++ và những ngôn ngữ khác, chúng ta có thể làm được điều này bằng cách sử dụng con trỏ hàm (function pointer) và con trỏ đến hàm thành viên. Không giống như con trỏ hàm như trong C/C++, ủy quyền là hướng đối tượng, kiểu dữ liệu an toàn (type-safe) và bảo mật.

Một điều thú vị và hữu dụng của ủy quyền là nó không cần biết và cũng không quan tâm đến những lớp đối tượng mà nó tham chiếu tới. Điều cần quan tâm đến những đối tượng đó là các đối mục của phương thức và kiểu trả về phải phù hợp với đối tượng ủy quyền khai báo.

Để tạo một ủy quyền ta dùng từ khóa delegate theo sau là kiểu trả về tên phương thức được ủy quyền và các đối mục cần thiết:

public delegate int WhichIsFirst(object obj1, object obj2);

Khai báo trên định nghĩa một ủy quyền tên là WhichIsFirst, nó sẽ đóng gói bất cứ phương thức nào lấy hai tham số kiểu object và trả về giá trị int.

Một khi mà ủy quyền được định nghĩa, chúng ta có thể đóng gói một phương thức thành viên bằng việc tạo một thể hiện của ủy quyền này, truyền vào trong một phương thức có khai báo kiểu trả về và các đối mục cần thiết.

Từ phần này về sau chúng ta quy ước có thể sử dụng qua lại giữa hai từ uỷ quyền và delegate với nhau.

Sử dụng ủy quyền để xác nhận phương thức lúc thực thi

Ủ y quyền như chúng ta đã biết là được dùng để xác định những loại phương thức có thể được dùng để xử lý các sự kiện và để thực hiện callback trong chương trình ứng dụng. Chúng cũng có thể được sử dụng để xác định các phương thức tĩnh và các instance của phương thức mà chúng ta không biết trước cho đến khi chương trình thực hiện.

Giả sử minh họa như sau, chúng ta muốn tạo một lớp chứa đơn giản gọi là Pair lớp này lưu giữ và sắp xếp hai đối tượng được truyền vào cho chúng. Tạm thời lúc này chúng ta cũng không thể biết loại đối tượng mà một Pair lưu giữ. Nhưng bằng cách tạo ra các phương thức bên trong các đối tượng này thực hiện việc sắp xếp và được ủy quyền, chúng ta có thể ủy quyền thực hiện việc sắp thứ tự cho chính bản thân của đối tượng đó.

Những đối tượng khác nhau thì sẽ sắp xếp khác nhau. Ví dụ, một Pair chứa các đối tượng đếm có thể được sắp xếp theo thứ tự số, trong khi đó một Pair nút lệnh button có thể được sắp theo thứ tự alphabe tên của chúng. Mong muốn của người tạo ra lớp Pair là những đối tượng bên trong của Pair phải có trách nhiệm cho biết thứ tự của chúng cái nào là thứ tự đầu tiên và thứ hai. Để làm được điều này, chúng ta phải đảm bảo rằng các đối tượng bên trong Pair phải cung cấp một phương thức chỉ ra cho chúng ta biết cách sắp xếp các đối tượng.

Chúng ta định nghĩa phương thức yêu cầu bằng việc tạo một ủy quyền, ủy quyền này định nghĩa ký pháp và kiểu trả về của phương thức đối tượng (như button) để cung cấp và cho phép Pair xác định đối tượng nào đến trước đầu tiên và đối tượng nào là thứ hai.

Lớp Pair định nghĩa một ủy quyền, WhichIsFirst. Phương thức Sort sẽ lấy một tham số là thể hiện của WhichIsFirst. Khi một đối tượng Pair cần biết thứ tự của những đối tượng bên trong của nó thì nó sẽ yêu cầu ủy quyền truyền vào hai đối tượng chứa trong nó như là tham số. Trách nhiệm của việc xác định thứ tự của hai đối tượng được trao cho phương thức đóng gói bởi ủy quyền.

Để kiểm tra thực hiện cơ chế ủy quyền, chúng ta sẽ tạo ra hai lớp, lớp Cat và lớp Student. Hai lớp này có ít điểm chung với nhau, ngoại trừ cả hai thực thi những phương thức được đóng gói bởi WhichIsFirst. Do vậy cả hai đối tượng này có thể được lưu giữ bên trong của đối tượng Pair.

Trong chương trình thử nghiệm này chúng ta sẽ tạo ra hai đối tượng Student và hai đối tượng Cat và lưu chúng vào mỗi một đối tượng Pair. Sau đó chúng ta sẽ tạo những đối tượng ủy quyền để đóng gói những phương thức của chúng, những phương thức này phải phù hợp với ký pháp và kiểu trả về của ủy quyền. Sau cùng chúng ta sẽ yêu cầu những đối tượng Pair này sắp xếp những đối tượng Student và Cat, ta làm từng bước như sau:

Bắt đầu bằng việc tạo phương thức khởi dựng Pair lấy hai đối tượng và đưa chúng vào trong từng mảng riêng:

public class Pair

{

// đưa vào 2 đối tượng theo thứ tự

public Pair( object firstObjectr, object secondObject)

{

thePair[0] = firstObject;

thePair[1] = secondObject;

}

// biến lưu giữ hai đối tượng

private object[] thePair = new object[2];

}

Tiếp theo là chúng ta phủ quyết phương thức ToString() để chứa giá trị mới của hai đối tượng mà Pair nắm giữ:

public override string ToString()

{

// xuất thứ tự đối tượng thứ nhất trước đối tượng thứ hai 

return thePair[0].ToString() +"," + thePair[1].ToString();

}

Bây giờ thì chúng ta đã có hai đối tượng bên trong của Pair và chúng ta có thể xuất giá trị của chúng ra màn hình. Tiếp tục là chúng ta sẽ thực hiện việc sắp xếp và in kết quả sắp xếp. Hiện tại thì không xác định được loại đối tượng mà chúng ta có, do đó chúng ta sẽ ủy quyền quyết định thứ tự sắp xếp cho chính bản thân các đối tượng mà Pair lưu giữ bên trong. Do vậy, chúng ta yêu cầu rằng mỗi đối tượng được lưu giữ bên trong Pair thực hiện việc kiểm tra xem đối tượng nào sắp trước. Phương thức này lấy hai tham số đối tượng và trả về giá trị kiểu liệt kê: theFirstComeFirst nếu đối tượng đầu tiên được đến trước và theSecondComeFirst nếu giá trị thứ hai đến trước.

Những phương thức yêu cầu sẽ được đóng gói bởi ủy quyền WhichIsFirst được định nghĩa bên trong lớp Pair:

public delegate comparison

WhichIsFirst( object obj1, object obj2);

Giá trị trả về là kiểu comparison đây là kiểu liệt kê:

public enum comparison

{

theFirstComesFirst = 1, theSecondComesFirst = 2

}

Bất cứ phương thức tĩnh nào lấy hai tham số đối tượng object và trả về kiểu comparison có thể được đóng gói bởi ủy quyền vào lúc thực thi.

Lúc này chúng ta định nghĩa phương thức Sort cho lớp Pair:

public void Sort( WhichIsFirst theDelegateFunc)

{

if (theDelegateFunc(thePair[0], thePair[1]) == comparison.theSecondComeFirst)

{

object temp = thePair[0];

thePair[0] = thePair[1];

thePair[1] = temp;

}

}

Phương thức này lấy một tham số: một ủy quyền có kiểu WhichIsFirst với tên là theDelegateFunc. Phương thức Sort giao phó trách nhiệm quyết định thứ tự đến trước sau của hai đối tượng bên trong Pair đến phương thức được đóng gói bởi ủy quyền. Bên trong thân của Sort, phương thức ủy quyền được gọi và trả về một giá trị, giá trị này là một trong hai giá trị liệt kê của comparison.

Nếu giá trị trả về là theSecondComesFirst, đối tượng bên trong của Pair sẽ được hoán đổi vị trí, trường hợp ngược lại thì không làm gì cả.

Hãy tưởng tượng chúng ta đang sắp xếp những Student theo tên. Chúng ta viết một phương thức trả về theFirstComesFirst nếu tên của sinh viên đầu tiên đến trước và the- SecondComesFirst nếu tên của sinh viên thứ hai đến trước. Nếu chúng ta đưa vào là “Amy, Beth” thì phương thức trả về kết quả là theFirstComesFirst. Và ngược lại nếu chúng ta truyền “Beth, Amy” thì kết quả trả về là theSecondComesFirst. Khi chúng ta nhận được kết quả theSecondComesFirst, phương thức Sort sẽ đảo hai đối tượng này trong mảng, và thiết lập là Amy ở vị trí đầu còn Beth ở vị trí thứ hai.

Tiếp theo chúng ta sẽ thêm một phương thức ReverseSort, phương thức này đặt các mục trong mảng theo thứ tự đảo ngược lại:

public void ReverseSort( WhichIsFirst theDeleagteFunc)

{

if ( theDelegateFunc( thePair[0], thePair[1]) ==comparison.theFirstComesFirst)

{

object temp = thePair[0]; thePair[0] = thePair[1]; 

thePair[1] = temp;

}

}

Việc thực hiện cũng tương tự như phương thức Sort. Tuy nhiên, phương thức thực hiện việc hoán đổi nếu phương thức ủy quyền xác định là đối tượng trước tới trước. Do vậy, kết quả thực hiện của phương thức là đối tượng thứ hai sẽ đến trước. Lúc này nếu chúng ta truyền vào là “Amy, Beth”, phương thức ủy quyền sẽ trả về theFirstComesFirst, và phương thức ReverseSort sẽ hoán đổi vị trí của hai đối tượng này, thiết lập Beth đến trước. Điều này cho phép chúng ta sử dụng cùng phương thức ủy quyền tương tự như Sort, mà không cần yêu cầu đối tượng hỗ trợ phương thức trả về giá trị được sắp ngược.

Lúc này điều cần thiết là chúng ta tạo ra vài đối tượng để sắp xếp. Ta tạo hai lớp đối tượng đơn giản như sau: lớp đối tượng Student và lớp đối tượng Cat. Gán cho đối tượng Student một tên vào lúc tạo:

public class Student
    {
    public Student (string name)
    {
    this.name = name;
    }
    }
    

Lớp đối tượng Student này yêu cầu hai phương thức, một là phương thức phủ quyết ToString(), và một phương thức khác được đóng gói như là phương thức ủy quyền.

Lớp Student phải phủ quyết phương thức ToString() để cho phương thức ToString() của lớp Pair sử dụng một cách chính xác. Việc thực thi này thì không có gì phức tạp mà chỉ đơn thuần là trả về tên của sinh viên:

public override string ToString()

{

return name;

}

Student cũng phải thực thi một phương thức hỗ trợ cho Pair.Sort() có thể ủy quyền xác định thứ tự của hai đối tượng xem đối tượng nào đến trước:

public static comparison WhichStudentComesFirst(Object o1, Object o2)

{

Student s1 = (Student) o1; Student s2 = (Student) o2;

return ( String.Compare( s1.name, s2.name) <0 ? comparison.theFirstComesFirst : comparison.theSecondComesFirst);

}

String.Compare là phương thức của .NET trong lớp String, phương thức này so sánh hai chuỗi và trả về một giá trị nhỏ hơn 0 nếu chuỗi đầu tiên nhỏ hơn chuỗi thứ hai và lớn hơn 0 nếu chuỗi thứ hai nhỏ hơn, và giá trị là 0 nếu hai chuỗi bằng nhau. Phương thức này cũng đã được trình bày trong chương 10 về chuỗi. Theo lý luận trên thì giá trị trả về là theFirstComesFirst chỉ khi chuỗi thứ nhất nhỏ hơn, nếu hai chuỗi bằng nhau hay chuỗi thứ hai lớn hơn, thì phương thức này sẽ trả về cùng giá trị là theSecondComesFirst.

Phương thức WhichStudentComesFirst lấy hai tham số kiểu đối tượng và trả vềgiá trị kiểu liệt kê comparison. Điều này để làm tương ứng và phù hợp với phương thức được ủy quyền Pair.WhichIsFirst.

Lớp thứ hai là Cat, để phục vụ cho mục đích của chúng ta, thì Cat sẽ được sắp xếp theo trọng lượng, nhẹ đến trước nặng. Ta có khai báo lớp Cat như sau:

public class Cat

{

public Cat( int weight)

{

this.weight = weight;

}

// sắp theo trọng lượng

public static comparison WhichCatComesFirst(Object o1, Object o2)

{

Cat c1 = (Cat) o1; Cat c2 = (Cat) o2;

return c1.weight > c2.weight ?

theSecondComesFirst :

theFirstComesFirst;

}

public override string ToString()

{

return weight.ToString();

}

// biến lưu giữ trọng lượng private int weight;

}

Cũng tương tự như lớp Student thì lớp Cat cũng phủ quyết phương thức ToString() và thực thi một phương thức tĩnh với cú pháp tương ứng với phương thức ủy quyền. Và chúng ta cũng lưu ý là phương thức ủy quyền của Student và Cat là không cùng tên với nhau. Chúng ta không cần thiết phải làm cùng tên vì chúng ta sẽ gán đến phương thức ủy quyền lúc thực thi.Ví dụ minh họa sau trình bày cách một phương thức ủy quyền được gọi.

Làm việc với ủy quyền.

-----------------------------------------------------------------------------

namespace Programming_CSharp

{

using System;

// khai báo kiểu liệt kê public enum comparison

{

theFirstComesFirst =1, theSecondComesFirst = 2

}

// lớp Pair đơn giản lưu giữ 2 đối tượng 

public class Pair

{

// khai báo ủy quyền

public delegate comparison WhichIsFirst( object obj1, object obj2);

// truyền hai đối tượng vào bộ khởi dựng

public Pair( object firstObject, object secondObject)

{

thePair[0] = firstObject;

thePair[1] = secondObject;

}

// phương thức sắp xếp thứ tự của hai đối tượng

// theo bất cứ tiêu chuẩn nào của đối tượng 

public void Sort( WhichIsFirst theDelegateFunc)

{

if (theDelegateFunc(thePair[0], thePair[1]) ==comparison.theSecondComesFirst)

{

object temp = thePair[0]; 

thePair[0] = thePair[1];

thePair[1] = temp;

}

}

// phương thức sắp xếp hai đối tượng theo

// thứ tự nghịch đảo lại tiêu chuẩn sắp xếp

public void ReverseSort( WhichIsFirst theDelegateFunc)

{

if (theDelegateFunc( thePair[0], thePair[1]) ==comparison.theFirstComesFirst)

{

object temp = thePair[0];

thePair[0] = thePair[1]; 

thePair[1] = temp;

}

}

// yêu cầu hai đối tượng đưa ra giá trị của nó

public override string ToString()

{

return thePair[0].ToString() + ", "+ thePair[1].ToString();

}

// mảng lưu 2 đối tượng

private object[] thePair = new object[2];

}

//lớp đối tượng Cat public class Cat

{

public Cat(int weight)

{

this.weight = weight;

}

// sắp theo thứ tự trọng lượng

public static comparison WhichCatComesFirst(Object o1, Object o2)

{

Cat c1 = (Cat) o1; Cat c2 = (Cat) o2;

return c1.weight > c2.weight ? comparison.theSecondComesFirst : comparison.theFirstComesFirst;

}

public override string ToString()

{

return weight.ToString();

}

// biến lưu trọng lượng private int weight;

}

// khai báo lớp Student public class Student

{

public Student( string name)

{

this.name = name;

}

// sắp theo thứ tự chữ cái

public static comparison WhichStudentComesFirst( Object o1, Object o2)

{

Student s1 = (Student) o1; Student s2 = (Student) o2;

return (String.Compare( s1.name, s2.name) <0 ? comparison.theFirstComesFirst : comparison.theSecondComesFirst);

}

public override string ToString()

{

return name;

}

// biến lưu tên private string name;

}

public class Test

{

public static void Main()

{

// tạo ra hai đối tượng Student và Cat

// đưa chúng vào hai đối tượng Pair 

Student Thao = new Student("Thao"); 

Student Ba = new Student("Ba");

Cat Mun = new Cat(5); Cat Ngao = new Cat(2);

Pair studentPair = new Pair(Thao, Ba); 

Pair catPair = new Pair(Mun, Ngao);

Console.WriteLine("Sinh vien 			: {0}", studentPair.ToString()); Console.WriteLine("Meo 			: {0}", catPair.ToString());

// tạo ủy quyền

Pair.WhichIsFirst theStudentDelegate = new

Pair.WhichIsFirst( Student.WhichStudentComesFirst); 

Pair.WhichIsFirst theCatDelegate = new

Pair.WhichIsFirst( Cat.WhichCatComesFirst);

// sắp xếp dùng ủy quyền 

studentPair.Sort( theStudentDelegate);

Console.WriteLine("Sau khi sap xep studentPair		:{0}", studentPair.ToString());

studentPair.ReverseSort(theStudentDelegate);

Console.WriteLine("Sau khi sap xep nguoc studentPair		:{0}", studentPair.ToString());

catPair.Sort( theCatDelegate);

Console.WriteLine("Sau khi sap xep catPair		:{0}", catPair.ToString());

catPair.ReverseSort(theCatDelegate);

Console.WriteLine("Sau khi sap xep nguoc catPair		:{0}", catPair.ToString());

}

}

}

-----------------------------------------------------------------------------

Kết quả:

Sinh vien : Thao, Ba

Meo : 5, 2

Sau khi sap xep studentPair : Ba, Thao

Sau khi sap xep nguoc studentPair : Thao, Ba

Sau khi sap xep catPair : 2, 5

Sau khi sap xep nguoc catPair : 5, 2

-----------------------------------------------------------------------------

Trong đoạn chương trình thử nghiệm trên chúng ta tạo ra hai đối tượng Student và hai đối tượng Cat sau đó đưa chúng vào hai đối tượng chứa Pair theo từng loại. Bộ khởi dựng của lớp Student lấy một chuỗi đại diện cho tên của sinh viên và bộ khởi dựng của lớp Cat thì lấy một số int đại diện cho trọng lượng của mèo.

Student Thao = new Student("Thao");

Student Ba = new Student("Ba");

Cat Mun = new Cat("5"); Cat Ngao = new Cat("2");

Pair studentPair = new Pair(Thao, Ba); 

Pair catPair = new Pair(Mun, Ngao);

Console.WriteLine("Sinh vien 			: {0}", studentPair.ToString()); Console.WriteLine("Meo 			: {0}", catPair.ToString());

Sau đó chương trình in nội dung chứa bên trong của hai đối tượng chứa Pair, và chúng ta có thể thấy thứ tự như sau:

Sinh vien : Thao, Ba

Meo : 5, 2

Thứ tự xuất hiện của nó chính là thứ tự đưa vào. Tiếp theo chúng ta khởi tạo hai đối tượng ủy quyền:

Pair.WhichIsFirst theStudentDelegate = new

Pair.WhichIsFirst( Student.WhichStudentComesFirst); 

Pair.WhichIsFirst theCatDelegate = new

Pair.WhichIsFirst( Student.WhichCatComesFirst);

Ủ y quyền đầu tiên theStudentDelegate được tạo ra bằng cách truyền vào một phương thức tĩnh tương ứng của lớp Student. Đối tượng ủy quyền thứ hai, theCatDelegate được một phương thức tĩnh của lớp Cat.

Bây giờ ta đã có các đối tượng ủy quyền, chúng ta truyền ủy quyền đầu tiên cho phương thức Sort của đối tượng Pair, và sau đó là phương thức ReverseSort. Kết quả được xuất ra màn hình:

Sau khi sap xep studentPair : Ba, Thao

Sau khi sap xep nguoc studentPair : Thao, Ba

Sau khi sap xep catPair : 2, 5

Sau khi sap xep nguoc catPair : 5, 2

Ủy quyền tĩnh

Như chúng ta đã thấy trong ví dụ minh hoạ 11.1 trước thì hai thể hiện phương thức ủy quyền được khai báo bên trong lớp gọi (chính xác là trong hàm Main của Test). Điều này có thể không cần thiết ta có thể sử dụng khai báo ủy quyền tĩnh từ hai lớp Student và Cat. Do vậy ta có thể bổ sung lớp Student bằng cách thêm vào:

public static readonly Pair.WhichIsFirst OrderStudents =new Pair.WhichIsFirst(Student.WhichStudentComesFirst);

Ý nghĩa của lệnh trên là tạo một ủy quyền tĩnh tên là OrderStudents và có thuộc tính chỉ đọc readonly. Việc thêm vào thuộc tính readonly để ghi chú rằng một khi trường đã được tạo ra thì không được bổ sung sau đó.

Tương tự như vậy chúng ta có thể tạo ủy quyền tĩnh cho Cat như sau:

public static readonly Pair.WhichIsFirst OderCats =

new Pair.WhichIsFirst( Cat.WhichCatComesFirst);

Bây giờ thì đã có hai trường tĩnh hiện diện bên trong các lớp Student và Cat, mỗi cái sẽ gắn với phương thức tương ứng bên trong lớp. Sau đó chúng ta có thể thực hiện ủy quyền mà không cần khai báo thể hiện ủy quyền cục bộ. Việc chuyển ủy quyền được thực hiện trong lệnh in đậm như sau:

studentPair.Sort( theStudentDelegate);

Console.WriteLine("Sau khi sap xep studentPair		:{0}", studentPair.ToString());

studentPair.ReverseSort(Student.OrderStudents);

Console.WriteLine("Sau khi sap xep nguoc studentPair		:{0}",

studentPair.ToString());

catPair.Sort( theCatDelegate);

Console.WriteLine("Sau khi sap xep catPair		:{0}", catPair.ToString());

catPair.ReverseSort(Cat.OrderCats);

Console.WriteLine("Sau khi sap xep nguoc catPair		:{0}", catPair.ToString());

Kết quả thực hiện tương tự như trong ví dụ trước

Sử dụng ủy quyền như thuộc tính

Đối với ủy quyền tĩnh thì chúng bắt buộc phải được tạo thể hiện, do tính chất tĩnh, mà không cần biết là chúng có được sử dụng hay không, như lớp Student và Cat trong ví dụ bên trên. Chúng ta có thể phát triển những lớp này tốt hơn bằng cách thay thế ủy quyền tĩnh từ trường thành thuộc tính.

Với lớp Student ta có thể chuyển khai báo:

public static readonly Pair.WhichIsFirst OrderStudent =new Pair.WhichIsFirst( Student.WhichStudentComesFirst);

thành khai báo như sau:

public static Pair.WhichIsFirst OrderStudents

{

get

{

return new Pair.WhichIsFirst( WhichStudentComesFirst);

}

}

Tương tự như vậy chúng ta thực hiện thay thế với lớp Cat:

public static Pair.WhichIsFirst OderCats

{

get

{

return new Pair.WhichIsFirst( WhichCatComesFirst);

}

}

Khi truyền cho phương thức thì không thay đổi:

studentPair.Sort( Student.OderStudents); 

catPair.Sort( Cat.OrderCats);

Khi thuộc tính OrderStudents được truy cập thì ủy quyền được tạo ra:

return new Pair.WhichIsFirst( WhichCatComesFirst);

Điều quan trọng ở đây là ủy quyền sẽ không được tạo cho đến khi nào nó được yêu cầu. Việc này cho phép lớp gọi (như lớp Test) quyết định khi nào cần thiết sử dụng một ủy quyền nhưng vẫn cho phép việc tạo ủy quyền là trách nhiệm của lớp Student hay lớp Cat.

Thiết lập thứ tự thi hành với mảng ủy quyền

Ủ y quyền có thể giúp chúng ta tạo ra một hệ thống trong đó người sử dụng có thể quyết định đến thứ tự của các hoạt động khi thực thi. Giả sử chúng ta có một hệ thống xử lý ảnh trong đó các ảnh có thể được thao tác bởi một phương pháp được định nghĩa tốt như là: làm mờ, làm đậm, xoay, lọc ảnh,... Giả sử rằng, thứ tự khi sử dụng các hiệu ứng này được áp dụng cho ảnh là quan trọng. Người sử dụng muốn lựa chọn những hiệu ứng này từ menu, anh ta chọn tất cả các hiệu ứng tùy thích, và sau đó yêu cầu bộ xứ lý ảnh thực hiện lần lượt các hiệu ứng mà anh ta đã xác định.

Chúng ta có thể tạo những ủy quyền cho mỗi hoạt động và sau đó thêm chúng vào một tập hợp được sắp, như là một mảng chẳng hạn, theo một thứ tự mà ta muốn chúng thực thi. Một khi tất cả các ủy quyền được tạo ra và đưa vào tập hợp, chúng ta dễ dàng lặp lần lượt qua các thành phần của mảng, và thực thi lần lượt từng phương thức ủy quyền.

Chúng ta bắt đầu bằng việc xây dựng một lớp Image thể hiện một ảnh sẽ được xử lý bởi lớp ImageProcessor:

public class Image

{

public Image()

{

Console.WriteLine("An image created");

}

}

Chúng ta có thể tưởng tượng rằng việc xuất ra chuỗi như vậy tương ứng với việc tạo một ảnh .gif hay .jpeg hay đại loại như vậy.

Sau đó lớp ImageProcessor khai báo một ủy quyền. Dĩ nhiên là chúng ta có thể định nghĩa một ủy quyền riêng trả về bất cứ kiểu dữ liệu nào hay lấy bất cứ tham số nào mà chúng ta muốn. Trong ví dụ này chúng ta định nghĩa một ủy quyền có thể đóng gói bất cứ phương thức không có giá trị trả về và cũng không nhận bất cứ tham số nào hết:

public delegate void DoEffect();

Tiếp tục lớp ImageProcessor khai báo một sô phương thức, và từng phương thức này phù hợp với ký pháp và kiểu trả về được khai báo bởi ủy quyền:

public static void Blur()
    {
    Console.WriteLine("Blurring image");
    }
    public static void Filter()
    {
    Console.WriteLine("Filtering image");
    }
    public static void Sharpen()
    {
    Console.WriteLine("Sharpening image");
    }
    public static void Rotate()
    {
    Console.WriteLine("Rotating image");
    }
    

Lớp ImageProcessor cần thiết có một mảng để lưu giữ các ủy quyền mà người sử dụng chọn, một biến lưu giữ số hiệu ứng được chọn và dĩ nhiên là có một biến ảnh để xử lý:

DoEffect[] arrayOfEffects; Image image;

int numEffectsRegistered = 0;

ImageProcessor cũng cần một phương thức để thêm các ủy quyền vào trong mảng:

public void AddToEffects( DoEffect theEffect)
    {
    if (numEffectsRegistered >=0)
    {
    throw new Exception("Too many members in array");
    }
    arrayOfEffects[numEffectsRegistered ++] = theEffect;
    }
    

Ngoài ra còn cần một phương thức thật sự gọi các ủy quyền này:

public void ProcessImage()
    {
    for (int i = 0; i < numEffectsRegistered; i++)
    {
    arrayOfEffects[i]();
    }
    }
    

Cuối cùng, chúng ta khai báo những ủy quyền tĩnh, để các client gọi, và chặn chúng lại để xử lý những phương thức:

public DoEffect BlurEffect = new DoEffect(Blur);

public DoEffect SharpenEffect = new DoEffect(Sharpen);

public DoEffect FilterEffect = new DoEffect(Filter);

public DoEffect RotateEffect = new DoEffect(Rotate);

Việc chọn các thao tác diễn ra trong quá trình tương tác ở thành phần giao diện người sử dụng. Trong ví dụ này chúng ta mô phỏng bằng cách chọn các hiệu ứng, thêm chúng vào trong mảng, và ProcessImage.

Sử dụng mảng ủy quyền.

-----------------------------------------------------------------------------

namespace Programming_CSharp

{

using System;

// khai báo lớp ảnh public class Image

{

public Image()

{

Console.WriteLine("An image created");

}

}

// lớp xử lý ảnh

public class ImageProcessor

{

// khai báo ủy quyền

public delegate void DoEffect();

// tạo các ủy quyền tĩnh

public DoEffect BlurEffect = new DoEffect(Blur);

public DoEffect SharpenEffect = new DoEffect(Sharpen);

public DoEffect FilterEffect = new DoEffect(Filter);

public DoEffect RotateEffect = new DoEffect(Rotate);

// bộ khởi dựng khởi tạo ảnh và mảng

public ImageProcessor(Image image)

{

this.image = image;

arrayOfEffects = new DoEffect[10];

}

// thêm hiệu ứng vào trong mảng

public void AddToEffects( DoEffect theEffect)

{

if (numEffectsRegistered >=0)

{

throw new Exception("Too many members in array");

}

arrayOfEffects[numEffectsRegistered ++] = theEffect;

}

// các phương thức xử lý ảnh 

public static void Blur()

{

Console.WriteLine("Blurring image");

}

public static void Filter()

{

Console.WriteLine("Filtering image");

}

public static void Sharpen()

{

Console.WriteLine("Sharpening image");

}

public static void Rotate()

{

Console.WriteLine("Rotating image");

}

// gọi các ủy quyền để thực hiện hiệu ứng 

public void ProcessImage()

{

for (int i = 0; i < numEffectsRegistered; i++)

{

arrayOfEffects[i]();

}

}

// biến thành viên

private DoEffect[] arrayOfEffects;

private Image image;

private int numEffectsRegistered = 0;

}

// lớp Test để kiểm chứng chương trình 

public class Test

{

public static void Main()

{

Image theImage = new Image();

// do không có GUI để thực hiện chúng ta sẽ chọn lần

// lượt các hành động và thực hiện

ImageProcessor theProc = new ImageProcessor(theImage); theProc.AddToEffects(theProc.BlurEffect); 

theProc.AddToEffects(theProc.FilterEffect); theProc.AddToEffects(theProc.RotateEffect); theProc.AddToEffects(theProc.SharpenEffect); 

theProc.ProcessImage();

}

}

}

-----------------------------------------------------------------------------

Kết quả:

An image created

Blurring image

Filtering image

Rotate image

Sharpening image

-----------------------------------------------------------------------------

Trong ví dụ trên, đối tượng ImageProcessor được tạo ra và những hiệu ứng được thêm vào. Nếu người dùng chọn làm mờ trước khi lọc ảnh, thì đơn giản là được đưa vào mảng ủy quyền theo thứ tự tương ứng. Tương tự như vậy, bất cứ hành động lựa chọn nào của người dùng mong muốn, ta đưa thêm nhiều ủy quyền vào trong tập hợp.

Chúng ta có thể tưởng tượng việc hiển thị thứ tự hành động này trong một danh sách listbox và cho phép người sử dụng sắp xếp lại phương thức, di chuyển chúng lên xuống trong danh sách. Khi các hành động này được sắp xếp lại thì chúng ta chỉ cần thay đổi thứ tự trong tập hợp. Ngoài ra ta cũng có thể đưa các hoạt động này vào trong cơ sở dữ liệu rồi sau đó đọc chúng lúc thực hiện.

Ủy quyền dễ dàng cung cấp động cho ta các phương thức được gọi theo một thứ tự xác định

Multicasting

Cơ chế multicasting cho phép gọi hai phương thức thực thi thông qua một ủy quyền đơn. Điều này trở nên quan trọng khi xử lý các sự kiện, sẽ được thảo luận trong phần cuối của chương.

Mục đích chính là có một ủy quyền có thể gọi thực hiện nhiều hơn một phương thức. Điều này hoàn toàn khác với việc có một tập hợp các ủy quyền, vì mỗi trong số chúng chỉ gọi được duy nhất một phương thức. Trong ví dụ trước, tập hợp được sử dụng để lưu giữ các ủy quyền khác nhau. Tập hợp này cũng có thể thêm một ủy quyền nhiều hơn một lần, và sử dụng tập hợp để sắp xếp lại các ủy quyền và điều khiển thứ tự hành động được gọi.

Với Multicasting chúng ta có thể tạo một ủy quyền đơn và cho phép gọi nhiều phương thức được đóng. Ví dụ, khi một nút lệnh được nhấn chúng ta có thể muốn thực hiện nhiều hơn một hàh động. Để làm được điều này chúng ta có thể đưa cho button một tập hợp các ủy quyền, nhưng để sáng rõ hơn và dễ dàng hơn là tạo một ủy quyền Multicast.

Bất cứ ủy quyền nào trả về giá trị void là ủy quyền multicast, mặc dù vậy ta có thể đối xử với nó như là ủy quyền bình thường cũng không sao. Hai ủy quyền Multicast có thể được kết hợp với nhau bằng phép toán cộng (+). Kết quả là một ủy quyền Multicast mới và gọi đến tất cả các phương thức thực thi nguyên thủy của cả hai bên. Ví dụ, giả sử Writer và Logger là ủy quyền trả về giá trị void, dòng lệnh theo sau sẽ kết hợp chúng lại với nhau và tạo ra một ủy quyền Multicast mới:

myMulticastDelegate = Writer + Logger;

Chúng ta cũng có thể thêm những ủy quyền vào trong ủy quyền Multicast bằng toán tử cộng bằng (+=). Phép toán này sẽ thêm ủy quyền ở phía bên phải của toán tử vào ủy quyền Multicast ở bên trái. Ví dụ minh họa như sau, giả sử có Transmitter và myMulticastDelegate là những ủy quyền, lệnh tiếp theo sau đây sẽ thực hiện việc thêm ủy quyền Transmitter vào trong myMulticastDelegate:

myMulticastDelegate += Transmitter;

Để hiểu rõ ủy quyền Multicast được tạo ra và sử dụng, chúng ta sẽ từng bước tìm hiểu thông qua ví dụ 11.3 bên dưới, trong ví dụ minh họa này chúng ta sẽ tạo ra một lớp có tên gọi là MyClassWithDelegate lớp này định nghĩa một delegate, delegate này lấy một tham số là chuỗi và không có giá trị trả về:

void delegate void StringDelegate( string s);

Sau đó chúng ta định một lớp gọi là MyImplementingClass lớp này có ba phương thức, tất cả các phương thức này đều trả về giá trị void và nhận một chuỗi làm tham số: WriteString, LogString, và Transmitting. Phương thức đầu tiên viết một chuỗi xuất ra màn hình tiêu chuẩn, chuỗi thứ hai mô phỏng viết vào một log file, và phương thức thứ ba mô phỏng việc chuyển một chuỗi qua Internet. Chúng ta tạo thể hiện delegate để gọi những phương thức tương ứng:

Writer("String passed to Writer
");

Logger("String passed to Logger
"); 

Transmitter("String passed to Transmitter
");

Để xem cách kết hợp các delegate, chúng ta tạo một thể hiện delegate

0