委託是一種安全地封裝方法的類型,它與 C 和 C++ 中的函數指針相似。與 C 中的函數指針不一樣,委託是面向對象的、類型安全的和保險的。委託的類型由委託的名稱定義。下面的示例聲明瞭一個名爲 Del 的委託,該委託能夠封裝一個採用字符串做爲參數並返回 void 的方法。this
public delegate void Del(string message);
構造委託對象時,一般提供委託將包裝的方法的名稱或使用匿名方法。實例化委託後,委託將把對它進行的方法調用傳遞給方法。調用方傳遞給委託的參數被傳遞給方法,來自方法的返回值(若是有)由委託返回給調用方。這被稱爲調用委託。能夠將一個實例化的委託視爲被包裝的方法自己來調用該委託。例如:
// Create a method for a delegate.
public static void DelegateMethod(string message)
{
System.Console.WriteLine(message);
}
// Instantiate the delegate.
Del handler = DelegateMethod;
// Call the delegate.
handler("Hello World");
委託類型派生自 .NET Framework 中的 Delegate 類。委託類型是密封的,不能從 Delegate 中派生委託類型,也不可能從中派生自定義類。因爲實例化委託是一個對象,因此能夠將其做爲參數進行傳遞,也能夠將其賦值給屬性。這樣,方法即可以將一個委託做爲參數來接受,而且之後能夠調用該委託。這稱爲異步回調,是在較長的進程完成後用來通知調用方的經常使用方法。以這種方式使用委託時,使用委託的代碼無需瞭解有關所用方法的實現方面的任何信息。此功能相似於接口所提供的封裝。有關更多信息,請參見什麼時候使用委託而不使用接口。
回調的另外一個常見用法是定義自定義的比較方法並將該委託傳遞給排序方法。它容許調用方的代碼成爲排序算法的一部分。下面的示例方法使用 Del 類型做爲參數:
public void MethodWithCallback(int param1, int param2, Del callback)
{
callback("The number is: " + (param1 + param2).ToString());
}
而後能夠將上面建立的委託傳遞給該方法:
MethodWithCallback(1, 2, handler);
在控制檯中將收到下面的輸出:
The number is: 3
在將委託用做抽象概念時,MethodWithCallback 不須要直接調用控制檯 -- 設計它時無需考慮控制檯。MethodWithCallback 的做用只是準備字符串並將該字符串傳遞給其餘方法。此功能特別強大,由於委託的方法可使用任意數量的參數。
將委託構造爲包裝實例方法時,該委託將同時引用實例和方法。除了它所包裝的方法外,委託不瞭解實例類型,因此只要任意類型的對象中具備與委託簽名相匹配的方法,委託就能夠引用該對象。將委託構造爲包裝靜態方法時,它只引用方法。考慮下列聲明:
public class MethodClass
{
public void Method1(string message) { }
public void Method2(string message) { }
}
加上前面顯示的靜態 DelegateMethod,如今咱們有三個方法可由 Del 實例進行包裝。
調用委託時,它能夠調用多個方法。這稱爲多路廣播。若要向委託的方法列表(調用列表)中添加額外的方法,只需使用加法運算符或加法賦值運算符(「+」或「+=」)添加兩個委託。例如:
MethodClass obj = new MethodClass();
Del d1 = obj.Method1;
Del d2 = obj.Method2;
Del d3 = DelegateMethod;
//Both types of assignment are valid.
Del allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;
此時,allMethodsDelegate 在其調用列表中包含三個方法 -- Method1、Method2 和 DelegateMethod。原來的三個委託 d1、d2 和 d3 保持不變。調用 allMethodsDelegate 時,將按順序調用全部這三個方法。若是委託使用引用參數,則引用將依次傳遞給三個方法中的每一個方法,由一個方法引發的更改對下一個方法是可見的。若是任一方法引起了異常,而在該方法內未捕獲該異常,則該異常將傳遞給委託的調用方,而且再也不對調用列表中後面的方法進行調用。若是委託具備返回值和/或輸出參數,它將返回最後調用的方法的返回值和參數。若要從調用列表中移除方法,請使用減法運算符或減法賦值運算符(「-」或「-=」)。例如:
//remove Method1
allMethodsDelegate -= d1;
// copy AllMethodsDelegate while removing d2
Del oneMethodDelegate = allMethodsDelegate - d2;
因爲委託類型派生自 System.Delegate,因此可在委託上調用該類定義的方法和屬性。例如,爲了找出委託的調用列表中的方法數,您能夠編寫下面的代碼:
int invocationCount = d1.GetInvocationList().GetLength(0);
在調用列表中具備多個方法的委託派生自 MulticastDelegate,這是 System.Delegate 的子類。因爲兩個類都支持 GetInvocationList,因此上面的代碼在兩種狀況下都適用。
多路廣播委託普遍用於事件處理中。事件源對象向已註冊接收該事件的接收方對象發送事件通知。爲了爲事件註冊,接收方建立了旨在處理事件的方法,而後爲該方法建立委託並將該委託傳遞給事件源。事件發生時,源將調用委託。而後,委託調用接收方的事件處理方法並傳送事件數據。給定事件的委託類型由事件源定義。有關更多信息,請參見事件(C# 編程指南)。
在編譯時,對分配了兩種不一樣類型的委託進行比較將產生編譯錯誤。若是委託實例靜態地屬於類型 System.Delegate,則容許進行比較,但在運行時將返回 false。例如:
delegate void Delegate1();
delegate void Delegate2();
static void method(Delegate1 d, Delegate2 e, System.Delegate f)
{
// Compile-time error.
//Console.WriteLine(d == e);
// OK at compile-time. False if the run-time type of f
//is not the same as that of d.
System.Console.WriteLine(d == f);
}