委託,在C#編程中佔有極其重要的地位,委託可以將函數封裝到委派物件中,並且多個委託可以合并為一個委託,委派物件則可以像普通對象一樣被儲存、傳遞,之後在任何時刻進行調用,因此,C#中函數回調機制的實現基本上依賴於委託。C#的delegate關鍵字用於聲明委託,它具有將聲明委託類型映射到System.Delegate類的能力,System.Delegate類位於mscorlib.dll中,是.NET的基礎核心類之一。使用delegate關鍵字聲明一個委託,實質上建立了System.Delegate的衍生類別,因此委託類型並非結構體也不是其它類型,它是一個類。一個委派物件也就是一個類的執行個體。以下是Delegate類的聲明:
複製代碼 代碼如下:
public abstract class Delegate
Delegate是所以委託類型的基類,C#中的多播委託實際上是MulticastDelegate類,它是System.Delegate的衍生類別,而本文中介紹的Action、Func泛型委派實際上都是MulticastDelegate類的衍生類別型。C#中當我們使用delegate關鍵字聲明一個委託類型時,實際上是由C#編譯器根據我們聲明時的方法簽名協助我們產生一個與簽名匹配的,派生自MulticastDelegate的類。在泛型大量應用之前,我們寫一個C#程式的時候可能會使用delegate關鍵字聲明許多委託類型,因為這些類型都對應於不同的方法簽名。通過Visual Studio的物件瀏覽器查看mscorlib可以看到這兩種重要的泛型委派:
其中除了Action之外,其它的委託都是泛型的,其實就是一些泛型類。這便是.NET核心庫中全部的泛型委派了。這些泛型委派分為Func、Action中,它們藉助於泛型特性,可以替代C#中幾乎所有的委託類型,也就是說一般情況下,在我們的程式中不必再聲明任何新的委託類型,就可以封裝所有的函數了。比如我們有兩個方法:
複製代碼 代碼如下:
public static void OtputString(string str)
{
Console.WriteLine(str);
}
public static int Add(int a, int b)
{
return a + b;
}
Func泛型委派與Action相比即多出了一個TResult型別參數,用於函數具有傳回值的情況,Action泛型委派用於沒有傳回值的函數。當我們要獲得這兩個方法的委派物件時這樣變可以了:
複製代碼 代碼如下:
var action = new Action<string>(OtputString);
action("OutputString Invoked!");
var func = new Func<int, int, int>(Add);
var sum = func(3, 5);
Console.WriteLine(sum);
可以看見,當我們將具有傳回值的函數封裝成委派物件時使用Func委託,如果函數沒有傳回值則使用Action,核心庫提供的泛型委派型別參數最短的為0,最長的為8個。因此,Action及其泛型委派可以匹配無傳回值、參數數量為0到8的任何函數。同樣的,Func泛型委派可以匹配由傳回值、參數數量在0到8個的任何函數。一般情況下,程式中函數的參數數量都不會超過8個,即使超過8個,我們可以聲明新的泛型委派類型來應對
複製代碼 代碼如下:
delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9>(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8, T9 p9);
使用這些泛型委派不會有任何的效能損失,使得程式中委託的使用風格保持一致。唯一的缺點就是類型的名稱無法表達具體的用途,舉例來講EventHandler委託,我們一看名字就知道這是用於事件處理的委託。而使用Action<object,EventArgs>委託我們則無法從名稱看出這種類型的委託是何種用途。
泛型委派有替代所有其它委託的能力,到底應該使用泛型委派還是普通委託、何時使用、在哪種情況下用,可能每個人都有不同的簡介,不過說到底,泛型委派能統一程式碼風格以及隨處方便使用等優點是非常顯著的。