當你在 C# 中宣告一個委托時,編譯器在幕後會自動生成一個完整的類別定義。以 public delegate void MyDelegate(); 為例,編譯器實際上會產生如下繼承自 System.MulticastDelegate 的類別:
public class MyDelegate : System.MulticastDelegate
{
// 建構函式
public MyDelegate(Object @object, IntPtr method);
// 與原始委托簽章相符的同步呼叫方法
public virtual void Invoke();
// 非同步呼叫的開始方法
public virtual IAsyncResult BeginInvoke(AsyncCallback callback, object @object);
// 非同步呼叫的結束方法
public virtual void EndInvoke(IAsyncResult result);
}
這個編譯器生成的類別包含四個核心方法:一個建構函式、Invoke、BeginInvoke 和 EndInvoke。其中建構函式與 Invoke 方法是理解委托運作機制的關鍵。
我們可以使用 ILDasm.exe (IL 反組譯工具) 來檢視編譯後的組件中繼資料,確認編譯器確實生成了這樣的類別結構。下圖展示了 ILDasm 中委托類別的元數據:
圖中可見,編譯器定義了一個名為 MyDelegate 的類別,繼承自 FCL (Framework Class Library) 中的 System.MulticastDelegate。所有委托類型皆繼承自 MulticastDelegate。
值得注意的是:System.MulticastDelegate 繼承自 System.Delegate,後者又繼承自 System.Object。FCL 中存在兩個委托基類是有歷史原因的,這在設計上略顯遺憾。儘管所有委托類型都以 MulticastDelegate 為基類,但在某些情況下,我們仍需使用 Delegate 類別定義的方法來操作委托實例。例如 Delegate.Combine 和 Delegate.Remove 這兩個靜態方法,其參數類型為 Delegate,因為繼承關係的存在,委托實例可以直接傳入這些方法。
委托類別具有 Public 可見性,這取決於原始碼中委托宣告的存取修飾符。委托可以在類別內部定義 (嵌套類別),也可以在命名空間層級全域定義。簡單來說,只要可以定義類別的地方,就可以定義委托。
由於所有委托類型都繼承 MulticastDelegate,它們共同擁有三個最重要的非公共欄位,這些欄位支撐了委托的多播調用、目標物件引用與方法指標等核心功能。