看了《CLR via C#》的17章委托後,為自己做一點淺顯的總結,也分享給需要的人。
.NET通過委托來提供一種回調函數機制,.NET委托提供了很多功能,例如確保回調方法是類型安全的(CLR重要目標)。委托好允許順序調用多個方法(委托鏈),並且支持調用靜態方法和實例方法。
委托的基本語法就不多說了。
internal delegate void Feedback(int value); public sealed class Program{ publick static void Main(){ } private static void DelegateDemo(){ Counter(1,2,new Feedback(WriteToConsole)); Program p=new Program p(); Counter(1,2,new Feedback(WriteToMsgBox)); } private static void Counter(int from, int to, Feedback fb){ for(int i=from;i<to;i++){ if(fb!=null) fb(i); } } private static void WriteToConsole(int val){ Console.WriteLine(val); } private void WriteToMsgBox(int val){ MessageBox.Show(val) } }
和普通調用靜態方法實例方法沒區別,如果需要回調靜態方法,那麼className.FuncName(); 如果需要回調實例方法,那麼Class_object.FuncName();
由於委托是類型安全的,它可以調用私用方法。
協變性和逆變性。
將一個方法綁定到委托的時候,C#和CLR都允許引用類型的協變性和逆變性,注意是引用類型哦。
delegate Object MyCallback(FileStream s); string SomeMethod(Stream s); //引用類型允許協變性,Stream是符合逆變性 int SomeOtherMethod(Stream s); //值類型不允許協變性
協變性是指方法能返回從委托的返回類型派生的一個類型,不能用於void。逆變性是指方法獲取的參數是委托的參數類型的基類。
委托鏈
Delegate.Combine(FirstDelegateObj,SecondDelegateObj); FirstDelegateObj+=SecondDelegateObj; //語法糖效果,等同於上一行
上面代碼就是在構造委托鏈。
委托鏈的原理大概就是:維護MulticastDelegate類中的三個重要的非公共字段中的_invocationList.這個字段維護了委托數組。
另外兩個字段就是_target.當委托包裝了一個靜態方法時,這個字段為null.當委托對象包裝了一個實例方法時,這個字段引用的是回調方法要操作的對象。
_methodPtr 一個內部的整數值,CLR用它標識要回調的方法。
在很多時候,執行委托鏈的過程中可能遭遇其中某個方法的阻塞和異常影響接下來的方法的執行。這個時候,我們的解決辦法是使用MulticastDelegate為我們提供的GetInvocationList方法,獲取由委托引用組成的數組,
每個委托引用只想鏈中的一個委托對象。我們可以通過遍歷來執行每一個委托。
委托鏈執行後只會返回最後一個回調方法所返回的值、
下面給出MulticastDelegate中Invoke方法的偽代碼實現,這個方法解釋了委托的執行過程和原理。
public int Invoke(int value) { int result; Delegate[] delegateSet= _invoctionList as Delegate[]; if(delegateSet !=null) { //委托鏈 foreach(Feedback d in delegateSet) { result=d(value); } } else { //在執行對象上調用這個回調方法 result= _methodPtr.Invoke(_target,value); //以上代碼接近實際代碼,但實際上C#表示不出來。 } return result; }