委託是什麼
在C語言中,有一個函式指標的概念:
returnType (*func)(T1, T2...)
在這樣一個語句中,我們定義了指向返回型別是returnType,引數為T1,T2...的函式的指標func,func實質上是一個指向函式對應記憶體地址的指標。 C#中的委託和函式指標概念很相近,它可以被理解為是.NET中型別安全的函式指標。委託允許我們在執行時指定呼叫的方法,但是該方法必須符合一定的規範(與所定義委託的返回型別和引數相同)。 實際上,.NET是在C語言函式指標的基礎上為函式新增了一個容器(本文中的委託),只有符合這個容器尺寸(返回值,引數列表相同)的函式才能被裝進這個容器。在Runtime,.NET會通過呼叫 容器程式碼來達成C語言中直接呼叫函式指標的目的。這也是委託為什麼是型別安全的原因。 有趣的是,委託只檢查傳入函式的返回值和引數,並不關注傳入函式是例項方法還是靜態方法,它能接收這兩種函式型別。
委託的使用
1.宣告
在使用委託之前,我們首先需要宣告它。 委託在定義時和關鍵字的使用方法類似,我們只需要在宣告函式的語句前面加一個delegate,就把這個語句變成了委託宣告語句,比如: delegate double TwoLongsOp(long first, long second);
定義了一個輸入兩個long引數,返回一個double值的委託 TwoLongsOp。
2.使用
要想使用委託,我們還需要定義一個滿足如上輸入輸出的函式,比如:
class MathOperations{
double static TwoLongsAdd( long first, long second){
return first + second;
}
}
複製程式碼
這裡定義一個靜態方法,然後,定義一個委託例項:
TwoLongsOp addOp = MathOperations.TwoLongsAdd;
這樣就完成了一個委託的初始化,addOp現在是一個引用MathOperations類中靜態方法TwoLongsAdd,我們可以在程式碼中使用這個委託呼叫TwoLongsAdd方法了:
static invokeDelegate(TwoLongsOp op, double first, double second){
Console.WriteLine($"LoingOperation Result is : {0}", op(first, second));
}
invokeDelegate(addOp, 1.0, 2.0);
複製程式碼
以上程式碼的執行結果是“LoingOperation Result is : 3.0 ”
3.Action 和 Func
這是C#的語法糖,它們的作用在於更簡潔地宣告和使用委託,節約程式碼空間和程式設計師時間。使用它們,我們就可以省去之前的宣告和賦值步驟了:
static invokeDelegate(Func<double, double,. double> op , double first, doube second){
Console.WriteLine($"LoingOperation Result is : {0}", op(first, second));
}
invokeDelegate(MathOperat ions.TwoLongsAdd, 1.0, 2.0);
複製程式碼
這段程式碼和以上的三段加在一起實現的功能是等價的,這讓委託的語法變得更簡潔了。
4.多播委託
和函式指標顯著不同的一點是,.NET允許我們包含多個函式呼叫,只需要呼叫一個委託,就可以一次執行這個委託中包含的所有方法,我們稱這個特性為多播委託。 需要注意的是,多播委託引用的函式的返回型別必須是void,否則,我們只能得到最後一個函式呼叫結果。具體的例子可以參考C#高階程式設計或者是msdn文件。
委託的實現
事實上,當我們宣告一個委託時,實際上定義了一個新類,一個派生於System.MulticastDelegate 的類,而System.MulticastDelegate 又派生於System.Delegate。這兩個類很有意思,.NET能夠建立派生於它們的類,程式設計師不能手動建立它們。如果你嘗試手動繼承一個Delegate或者MulticastDelegate類,會看到這樣的錯誤資訊:
Error CS0644:'XXXXClass' cannot derive from special class 'MulticastDelegate'
Delegate 和 MulticastDelegate被.NET定義為特殊類,我們無法建立一個派生自特殊類的自定義類。 觀察C#的原始碼(source.roslyn.codeplex.com/#Microsoft.…),我們可以發現,微軟用一個enum型別SpecialType直接hardcode了所有的特殊類,實現方式相當巨硬。。。