字段:_target
類型:System.Object
描述:當委托對象封裝一個靜態方法時,這個字段為 null。當委托對象封裝一個實例方法時,這個字段引用的是調用回調方法是要操作的對象。換而言之,這個字段指名要傳給實例方法的隱式 this 參數的值。
字段:_methodPtr
類型:System.IntPtr
描述:一個內部的整數值,CLR用它標識要回調的方法。
字段:_invocationList
類型:System.Object
描述:該字段通常為null。在構建一個委托鏈時,它可以引用一個委托數組。
注意,所有委托都有一個構造器,該函數取2個參數:一個是對象引用,另一個是引用回調方法的一個整數。然而,如果仔細查看代碼1-1的源代碼,會發現傳遞的是 TestMethod。根據我們所學的編程知識判斷,這段代碼不會通過編譯!
然而,C#編譯器知道正在構建的是委托,所以會解析源代碼以確定引用的是哪個對象和方法。對象引用被傳遞給構造器的aobject參數,一個特殊的標識方法的 IntPtr值(從MethodDef 或 MemberRef 元數據標記獲得)被傳遞給method 參數。對於靜態方法,null 被傳遞給 object 參數。在構造器內部,這兩個參數分別保存在 _target 和 _methodPtr 私有字段內。
除此之外,構造器還將 _invocationList 字段設置為 null。
如此看來,每個委托對象實際封裝了一個方法和調用該方法時要操作的一個對象。
知道了委托對象如何構造,並了解了其內部結構之後,我們要談談回調方法是如何調用的。 為了方便起見,下面重復了代碼1-1中的 Method(MyDelegate aMyDelegate)方法的代碼:
private void Method(MyDelegate aMyDelegate)
{
if (aMyDelegate != null)
{
aMyDelegate();
}
}
if 語句首先檢查 aMyDelegate 是否為 null。如果 aMyDelegate 不為 null,你會看到下一行代碼調用了回調方法。null 檢查必不可少,因為 aMyDelegate 實際上只是一個可能引用 MyDelegate 委托對象的變量,它也可能為 null。這段代碼看上去就像是調用了一個名為 aMyDelegate 的函數。但是,這裡沒有名為 aMyDelegate 的函數。再次重申,因為編譯器知道 aMyDelegate 是一個引用了一個委托對象的變量,所以會生成代碼調用該委托對象的Invoke方法。換而言之,編譯器看到以下代碼時:
aMyDelegate();
將生成一下代碼,好像源代碼本來就是這麼寫的一樣:
aMyDelegate.Invoke();