Delegate có nghĩa là ủy quyền hay ủy thác. Trong lập trình đôi lúc ta gặp tình huống phải thực thi một hành động nào đó, nhưng lại không biết sẽ gọi phương thức nào của đối tượng nào. Chằng hạn, một nút nhấn button khi được nhấn phải thông báo cho đối tượng nào đó biết, nhưng đối tượng này không thểđược tiên đoán trong lúc cài đặt lớp button. Vì vậy ta sẽ kết nối lớp button với một đối tượng ủy thác và ủy thác (hay thông báo) cho đối tượng này trách nhiệm thực thi khi button được nhấn. Đối tượng ủy thác sẽđược gán (đăng ký ủy thác) vào thời điểm khác thích hợp. Event có nghĩa là sự kiện. Ngày nay mô hình lập trình giao diện người dùng đồ họa (Graphical User Interface - GUI) đòi hỏi cách tiếp cận theo hướng sự kiện. Một ứng dụng ngày nay hiển thị giao diện người dùng và chờ người dùng thao tác. Ứng với mỗi thao tác như chọn một trình đơn, nhấn một nút button, nhập liệu vào ô textbox … sẽ một sự kiện sẽ phát sinh. Một sự kiện có nghĩa là có điều gì đó đã xảy ra và chương trình phải đáp trả.
Delegate và event là hai khái niệm có liên quan chặt chẽ với nhau. Bởi vì để quản lý các sự kiện một cách mềm dẻo đòi hỏi các đáp trả phải được phân phối đến các trình giải quyết sự kiện. Trình giải quyết sự kiện trong C# được cài đặt bằng delegate.
Delegate còn được sử dụng như một hàm callback. Hàm callback là hàm có thể được tự động gọi bởi hàm khác. Công dụng thứ hai này của delegate được đề cập trong chương 19.
12.1 Delegate (ủy thác, ủy quyền)
Trong C#, delegate được hỗ trợ hoàn toàn bởi ngôn ngữ. Về mặt kỹ thuật, delegate thuộc kiểu tham chiếu được dùng để đóng gói phương thức đã xác định kiểu trả về và số lượng, kiểu tham số. Chúng ta có thểđóng gói bất kỳ phương thức thức nào phù hợp với phương thức của delegate. (Trong C++ có kỹ thuật tương tự là con trỏ hàm, tuy nhiên delegate có tính hướng đối tượng và an toàn về kiểu) Một delegate có thểđược tạo bắng từ khóa delagate, sau đó là kiểu trả về, tên delegate và các tham số của phương thức mà delegate chấp nhận:
public delegate int WhichIsFirst(object obj1, object obj2)
Dòng trên khai báo một delegate tên là WhichIsFirst có thể đóng gói (nhận) bất kỳ một phương thức nào nhận vào hai tham số kiểu object và trả về kiểu int.
Delegate và Event Gvhd: Nguyễn Tấn Trần Minh Khang
Khi một delegate được định nghĩa, ta có thể đóng gói một phương thức với delegateđó bằng cách khởi tạo với tham số là phương thức cho delegate.
12.1.1 Dùng delegate để xác định phương thức vào lúc chạy
Delegate được dùng để xác định (specify) loại (hay kiểu) của các phương thức dùng để quản lý các sự kiện; hoặc để cài đặt các hàm callback trong ứng dụng. Chúng cũng được dùng để xác định các phương thức tĩnh và không tĩnh (còn gọi là phương thức thề hiện - instance methods: là phương chỉ gọi được thông qua một thể hiện của lớp) chưa biết trước vào lúc thiết kế (có nghĩa là chỉ biết vào lúc chạy).
Ví dụ, giả sử chúng ta muốn tạo một lớp túi chứa đơn giản có tên là Pair (một cặp). Lớp này chứa 2 đối tượng được sắp xếp. Chúng ta không biết trước được đối tượng nào sẽđược truyền vào cho một thể hiện của lớp Pair, vì vậy không thể xây dựng hàm sắp xếp tốt cho tất cả các trường hợp. Tuy nhiên ta sẽ đẩy trách nhiệm này cho đối tượng bằng cách tạo phương thức mà công việc sắp xếp có thểủy thác. Nhờ đó ta có thể sắp thứ thự của các đối tượng chưa biết bằng cách ủy thác trách nhiệm này chính phương thức của chúng.
Ta định nghĩa một delegatecó tên WhichIsFirsttrong lớp Pair. Phương thức sort sẽ nhận một tham số kiểu WhichIsFirst. Khi lớp Pair cần biết thứ tự của đối tượng bên trong, nó sẽ gọi delegate với hai đối tượng làm tham số. Trách nhiệm quyết định xem đối tượng nào trong 2 đối tượng có thứ tự trước được ủy thác cho phương thức được đóng gói trong delegate.
Để kiểm thử delegate, ta tạo ra hai lớp: Dog và Student. Lớp Dog và Student không giống nhau ngoại trừ cả hai cùng cài đặt phương thức có thể được đóng gói bới WhichIsFirst, vì vậy cả Dog lẫn Student đều thích hợp được giữ trong đối tượng Pair.
Để kiểm thử chương trình chúng ta tạo ra một cặp đối tượng Student và một cặp đối tương Dog và lưu trữ chúng trong hai đối tượng Pair. Ta sẽ tạo một đối tượng delegate để đóng gói cho từng phương thức, sau đó ta yêu cầu đối tượng Pair sắp xếp đối tượng Dog và Student. Sau đây là các bước thực hiện:
public class Pair {
// cặp đối tượng
public Pair(object firstObject, object secondObject) {
thePair[0] = firstObject; thePair[1] = secondObject; }
// biến lưu giữ hai đối tượng
private object[]thePair = new object[2]; Kế tiếp, ta override hàm ToString()
public override string ToString( ) {
Delegate và Event Gvhd: Nguyễn Tấn Trần Minh Khang
}
Chúng ta đã có hai đối tượng trong lớp Pair và có thể in chúng ra. Bây giờ là phần sắp xếp chúng và in kết quả sắp xếp. Chúng ta không thể biết trước sẽ có loại đối tượng nào, và vì vậy chúng ta sẽủy thác quyền quyết định đối tượng nào có thứ tự trước cho chính các đối tượng. Như vậy ta sẽ yêu cầu đối tượng được xếp thứ tự trong lớp Pair phải cài đặt phương thức cho biết trong hai đối tượng, đối tượng nào có thứ tự trước. Phương thức này sẽ nhận vào hai đối tượng (thuộc bất kỳ loại nào) và trả về kiểu liệt kê: theFirstComeFirst nếu đối tượng đầu có thứ tự trước và theSecondComeFirst nếu đối tượng sau có thứ tự trước.
Những phương thức này sẽ được đóng gói bởi delegate WhichIsFirst định nghĩa trong lớp Pair.
public delegate comparisn WhichIsFirst(object obj1,object obj2) Trị trả về thuộc kiểu kiểu liệt kê comparison.
public enum comparison {
theFirstComesFirst = 1, theSecondComesFirst = 2 }
Bất kỳ một phương thức tĩnh nào nhận hai tham số kiểu object và trả về kiểu comparison đều có thểđược đóng gói bởi delegate này vào lúc chạy.
Bây giờ ta định nghĩa phương thức Sort của lớp Pair
public void Sort(WhichIsFirst theDelegatedFunc) {
if ( theDelegatedFunc(thePair[0],thePair[1]) ==
comparison.theSecondComesFirst )
{
object temp = thePair[0]; thePair[0] = thePair[1]; thePair[1] = temp;
} } }
Phương thức này nhận một tham số delegate tên WhichIsFirst. Phương thức Sort ủy thác quyền quyết định đối tượng nào có thứ tự trước cho phương thức được đóng gói trong delegate. Trong thân hàm Sort(), có lời gọi phương thức được ủy thác và xác định giá trị trả về.
Nếu trị trả về là theSecondComesFirst, hai đối tượng trong Pair sẽ hoán chuyển vị trí, ngược lại không có gì xảy ra.
Chúng ta sẽ xắp xếp các sinh viên theo thứ tự tên. Chúng ta phải viết một phương thức trả về theFirstComesFirst nếu tên của sinh viên đầu có thứ tự trước và ngược lại theSecondComesFirst nếu tên sinh viên sau có thứ tự trước. Nếu hàm trả về theSecondComesFirst ta sẽ thực hiện việc đảo vị trí của hai sinh viên trong Pair.