假定您在為一家新計算機公司起草業務計劃,面對如何處理產品分發的問題。當然,您從未考慮過親自將每一件新產品交付到每位客戶的手中。相反,您會將此責任委托給像 FedEx 或 UPS 等配送服務機構。您必須事先做三個決定:該行動是什麼(交付產品)、參數是什麼(客戶地址)以及返回值是什麼(付款)。然後,當實際有一個包裹要交付時,您就可以將責任委托給某個特定的配送服務機構。 在 Microsoft .NET 編程中,您會面對需要執行某種特定操作,但是無法預先知道將調用何種方法來執行該操作的情況。例如,在單擊某個按鈕時,可能需要調用某個方法,但是在設計該按鈕時,卻無法知道將調用哪個方法。您需要有一種方法來描述要調用何種方法。這正是委托派上用場的地方。在本文中,我將探索如何在 Visual Basic .NET 中使用委托。 入門 委托是一種引用類型,表示帶有特定簽名和返回類型的方法。可以在該委托中封裝任何匹配的方法。另外,還可以將委托看作是對方法的引用。 要了解其工作方式,我們來看一個委托解決的問題。正如您將在圖 1 中看到的,我將創建一個 Pair 類的簡單集合,其中有兩個對象。Pair 類創建一個名為 thePair 的私有數組,其中包括兩個成員: Public Class Pair Private thePair(2) As Object 構造函數接受兩個對象,然後按接收順序將其添加到此內部數組中: Public Sub New(ByVal firstObject As Object, ByVal secondObject As Object) thePair(0) = firstObject thePair(1) = secondObject End Sub Pair 提供其它三種方法:Sort、ReverseSort 以及 ToString 的重寫。Sort 方法將對內部數組中的兩個對象進行排序。當然,您不希望讓 Pair 類知道對這兩個對象進行排序的測試,因為實際上您可能在 Pair 中存儲任何類型的對象(Students、Dogs、Employees、Buttons,等等)。Pair 如何才能知道如何對所有這些不同類型的對象進行排序呢? 解決方案是將責任(確定哪個對象最小)委托給對象自己。Pair 如何實現這一點?使用委托(參見圖 1)。 在 Pair 類中,我已經定義了一個委托以封裝(引用)比較兩個對象的方法,然後確定哪個較小(不管“較小”是如何定義的,確定的比較類都是合適的): Public Delegate Function WhichIsSmaller( _ ByVal obj1 As Object, ByVal obj2 As Object) As Comparison 這是一個相當復雜的定義。讓我們來一段一段地查看這個定義: • Public 關鍵字將該委托聲明為 Pair 類的一個公共成員。 • Delegate 關鍵字表示您正在創建一個委托(而不是一個方法或屬性)。 • Function 關鍵字表示該委托將用於封裝一個函數(而不是一個子程序)。 • WhichIsSmaller 標識符是該委托的名稱。 • 括號內的值是該委托將封裝的方法的簽名。即該委托可能封裝任何接受兩個對象作為參數的函數。 • 最後的關鍵字 As Comparison 是該委托可能封裝的方法的返回類型。Comparison 是一個在 Pair 類中定義的枚舉: Public Enum Comparison theFirst = 1 theSecond = 2 End Enum 用該委托封裝的方法必須返回 Comparison.theFirst 或 Comparison.theSecond。 總之,剛剛展示的語句在名為 WhichIsSmaller 的 Pair 類中定義了一個公共委托,它封裝接受兩個對象作為參數的函數並返回 Comparison 枚舉類型的一個實例。 可以在該委托的實例中封裝任何匹配的方法。例如,您的 Pair 集合可能包含兩個 Student 對象,如下所示: Public Class Student Private name As String Public Sub New(ByVal name As String) Me.name = name End Sub ' other Student methods here End Class 您的 Student 類必須創建一個與 WhichIsSmaller 委托相匹配的方法。例如,您可以創建一個 WhichStudentIsSmaller 方法(參見圖 2 中的代碼)。該方法匹配所要求的簽名;它接受兩個對象作為參數,並且返回一個 Comparison 值。 因為我的 WhichStudentIsSmaller 方法需要將這些參數用作 Student 對象,而不是作為更一般的 Object 類型,我將把這些參數強制轉換為 Student。這是類型安全的,因為我決不會用其他任何類型的參數來調用該方法。 一旦我強制轉換了這兩個對象,我就可以對它們進行比較。在本例中,我將按字母順序比較它們的名稱值,並返回合適的枚舉值:Pair.Comparison.theFirst 或 Pair.Comparison.theSecond。 按字母順序的比較是通過調用 String 類的共享方法 Compare 來完成的。如果按照字母表,第一個字符串排在第二個前面(s1.name 的字母順序在 s2.name 之前),則 Compare 返回一個負整數值。如果按照字母表,第二個字符串排在第一個前面,則 Compare 返回一個正整數值,如果相同則返回零。 其他類也能創建與 WhichIsSmaller 委托相匹配的方法。例如,我還可以創建 Dog 類: Public Class Dog Private weight As Integer Public Sub New(ByVal weight As Integer) Me.weight = weight End Sub ' other Dog methods here End Class 該 Dog 類將實現一個方法,基於重量對兩個 Dog 實例進行比較: Public Shared Function WhichDogIsSmaller( _ ByVal o1 As Object, ByVal o2 As Object) As Pair.comparison Dim d1 As Dog = DirectCast(o1, Dog) Dim d2 As Dog = DirectCast(o2, Dog) If d1.weight > d2.weight Then
Return Pair.Comparison.theSecond Else Return Pair.Comparison.theFirst End If End Function 現在該 Pair 類就可以創建其 Sort 方法了。它將接受一個 WhichIsSmaller 委托作為參數: Public Sub Sort(ByVal theDelegatedFunc As WhichIsSmaller) If theDelegatedFunc(thePair(0), thePair(1)) = _ Comparison.theSecond Then Dim temp As Object = thePair(0) thePair(0) = thePair(1) thePair(1) = temp End If End Sub Sort 方法通過委托來調用委托的方法,傳遞 Pair 數組的兩個成員,返回一個枚舉值。如果該值為 Comparison.theSecond,就知道第二個對象比第一個類型小。Sort 方法甚至無需知道這兩個對象的類型就可以知道這一點!然後它可以將這兩個對象顛倒過來。如果委托的方法返回 Comparison.theFirst,則無需交換。 實例化委托 要對此進行測試,可以創建兩個 Student 對象,如下所示: Dim Jesse As New Student("Jesse") Dim Stacey As New Student("Stacey") 然後將其添加到一個新的 Pair 對象中: Dim studentPair As New Pair(Jesse, Stacey)隨後,可以實例化一個 WhichIsSmaller 委托,傳遞知道如何比較這兩個 Student 對象的 Student 的匹配方法: Dim theStudentDelegate As New _ Pair.WhichIsSmaller(AddressOf Student.WhichStudentIsSmaller) 現在我可以將這個委托傳遞給 Sort 方法,以便對這個兩個學生進行排序: studentPair.Sort(theStudentDelegate) 類似地,我可以創建兩個 Dog 對象,將其添加到 Pair,並基於比較兩個 Dog 的 Dog 方法實例化一個委托,然後將該委托傳遞給 Dog 對的排序方法,如以下各行所示: ' make two dogs Dim Milo As New Dog(65) Dim Fred As New Dog(12) ' store the two dogs in a Pair Dim dogPair As New Pair(Milo, Fred) ' instantiate a delegate Dim theDogDelegate As New _ Pair.WhichIsSmaller(AddressOf Dog.WhichDogIsSmaller) ' invoke Sort, pass in the delegate dogPair.Sort(theDogDelegate) Pair 類有一個接受相同的委托作為 Sort 的 ReverseSort。我同樣可以將您剛剛創建的 Student 和 Dog 委托傳遞給 ReverseSort: studentPair.ReverseSort(theStudentDelegate)dogPair.ReverseSort(theDogDelegate) 您可能已經猜到了,ReverseSort 的邏輯與 Sort 的邏輯相反。即,僅在比較方法返回的值指示第一個對象比第二個對象小的情況下,才交換 Pair 中的這兩項。 實例方法與委托 在圖 1所示示例中,封裝了 Dog 和 Student 類的共享方法。相反,可以同樣容易地將封裝的方法聲明為實例方法(即,非共享方法): Public Comparison WhichStudentIsSmaller(o1 as Object, o2 as Object) 盡管可以將該方法封裝在委托中,但是必須通過實例而不是通過類來引用它,如下所示: Dim theStudentDelegate As _ New Pair.WhichIsSmaller(AddressOf Jesse.WhichStudentIsSmaller) 盡管重復調用一個封裝了實例方法的委托比調用一個靜態方法效率更高,您還是需要一個實例來調用該方法。 共享委托 中聲明委托的方法的一個缺點是,調用類 (Tester) 必須實例化所需