轉至:herehtml
引言c#
Delegate是Dotnet1.0的時候已經存在的特性了,但因爲在實際工做中一直沒有機會使用Delegate這個特性,因此一直沒有對它做整理。這兩天,我再度翻閱了一些關於Delegate的資料,並開始正式整理這個C#中著名的特性。本文將由淺入深的談一下Delegate這個特性。設計模式
一.Delegate是什麼?安全
Delegate中文翻譯爲「委託」。Msdn中對Delegate的解釋以下:異步
C#中的委託相似於C或C++中的函數指針。使用委託使程序員能夠將方法引用封裝在委託對象內。而後能夠將該委託對象傳遞給可調用所引用方法的代碼,而沒必要在編譯時知道將調用哪一個方法。與C或C++中的函數指針不一樣,委託是面向對象、類型安全的,而且是安全的。函數
若是你是第一次接觸Delegate這個概念,你可能會對上面這段文字感受不知所云,不過沒關係,你能夠先把Delegate認爲就是一個函數指針。spa
而當你面對一個虛無的概念時,最好的應對方法就是直接看實例。下面一個簡單的Delegate使用例子。.net
class Program
{
static void OtherClassMethod(){ Console.WriteLine("Delegate an other class's method"); } static void Main(string[] args) {
TestDelegate test = new TestDelegate();
test.delegateMethod = new TestDelegate.DelegateMethod(test.NonStaticMethod);
test.delegateMethod += new TestDelegate.DelegateMethod(TestDelegate.StaticMethod);
test.delegateMethod += Program.OtherClassMethod;
test.RunDelegateMethods();
}
}
class TestDelegate
{
public delegate void DelegateMethod(); //聲明瞭一個Delegate Type
public DelegateMethod delegateMethod; //聲明瞭一個Delegate對象
public static void StaticMethod()
{
Console.WriteLine("Delegate a static method");
}
public void NonStaticMethod()
{
Console.WriteLine("Delegate a non-static method");
}
public void RunDelegateMethods()
{
if(delegateMethod != null)
{
Console.WriteLine("---------");
delegateMethod.Invoke();
Console.WriteLine("---------");
}
} }
上面是一個Delegate的使用例子,運行看看結果吧。下面我稍微解釋一下:翻譯
【1】public delegate void DelegateMethod();這裏聲明瞭一個Delegate的類型,名爲DelegateMethod,這種Delegate類型能夠搭載:返回值爲void,無傳入參數的函數。
【2】public DelegateMethod delegateMethod;這裏聲明瞭一個DelegateMethod的對象(即,聲明瞭某種Delegate類型的對象)。
區分:DelegateMethod是類型,delegateMethod是對象。
【3】爲何上面說Delegate能夠看作是函數指針呢?看下面這段代碼:
test.delegateMethod = new TestDelegate.DelegateMethod(test.NonStaticMethod);
test.delegateMethod += new TestDelegate.DelegateMethod(TestDelegate.StaticMethod); test.delegateMethod += Program.OtherClassMethod;
這裏delegateMethod搭載了3個函數,並且能夠經過調用delegateMethod.Invoke();運行被搭載的函數。這就是Delegate能夠看做爲函數指針的緣由。上面這段代碼中,delegateMethod只能搭載:返回值爲void,無傳入參數的函數(見:NonStaticMethod,StaticMethod,OtherClassMethod的定義),這和Delegate類型聲明有關(見DelegateMethod的聲明:public delegate void DelegateMethod())。
【4】Delegate在搭載多個方法時,能夠經過+=增長搭載的函數,也能夠經過-=來去掉Delegate中的某個函數。
二.Delegate和C++中函數指針的區別
Delegate和C++中的函數指針很像,但若是深刻對比,發現其實仍是有區別的,區別主要有三個方面(參考Stanley B. Lippman的一篇文章)
1) 一個 delegate對象一次能夠搭載多個方法(methods),而不是一次一個。當咱們喚起一個搭載了多個方法(methods)的delegate,全部方法以其「被搭載到delegate對象的順序」被依次喚起。
2) 一個delegate對象所搭載的方法(methods)並不須要屬於同一個類別。一個delegate對象所搭載的全部方法(methods)必須具備相同的原型和形式。然而,這些方法(methods)能夠即有static也有non-static,能夠由一個或多個不一樣類別的成員組成。
3) 一個delegate type的聲明在本質上是建立了一個新的subtype instance,該 subtype 派生自 .NET library framework 的 abstract base classes Delegate 或 MulticastDelegate,它們提供一組public methods用以詢訪delegate對象或其搭載的方法(methods) ,與函數指針不一樣,委託是面向對象、類型安全而且安全的。
看完上面關於Delegate的介紹,相信你們對它也有所瞭解了,下面咱們將進行更深刻地討論!
三.Delegate何時該用?
看完上面的介紹,你能夠會有一些疑問,爲何會有Delegate?實際中何時會用到?何時應該去用? 在回答這些問題以前,你們能夠先看看下面這段代碼:
class Program
{
static void Main(string[] args) { var car = new Car(15); new Alerter(car); car.Run(120); } } class Car { public delegate void Notify(int value); public event Notify notifier; private int petrol = 0; public int Petrol { get { return petrol; } set { petrol = value; if (petrol < 10) //當petrol的值小於10時,出發警報 { if (notifier != null) { notifier.Invoke(Petrol); } } } } public Car(int petrol) { Petrol = petrol; } public void Run(int speed) { int distance = 0; while (Petrol > 0) { Thread.Sleep(500); Petrol--; distance += speed; Console.WriteLine("Car is running... Distance is " + distance.ToString()); } } } class Alerter { public Alerter(Car car) { car.notifier += new Car.Notify(NotEnoughPetrol); } public void NotEnoughPetrol(int value) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("You only have " + value.ToString() + " gallon petrol left!"); Console.ResetColor(); } }
看完了上面的代碼後,你可能會問:爲何不在public int Petrol中直接調用Alerter.NotEnoughPetrol呢?由於Car模塊和Alerter模塊自己是兩個獨立的子系統,若是直接調用,耦合性就會增長,這不是咱們願意看到的。
其實以上的代碼是設計模式中的觀察者模式(觀察者模式又稱Source/Listener模式)的實現,當汽車在運行中汽油量<10時,警報器便會發出警報。在上面代碼中,Delegate至關於一個存放回調函數的函數指針,使用Delegate,咱們能夠很是方便地實現觀察者模式。而其實,在須要使用回調函數時,咱們均可以考慮使用Delegate。
不知道你有沒有發如今上面的代碼中還有一個問題呢?
public event Notify notifier;
上面的代碼中,咱們定義了一個Event,而事實上:
public Notify notifier;
這樣寫,也徹底能夠知足咱們的需求,這就引出了咱們的另外一個問題,Delegate和Event!
四.Delegate與Event
【1】Delegate和Event的關係
看微軟的代碼時,咱們會發現Delegate和Event這兩個關鍵字常常會一塊兒出現!究竟他們是什麼關係呢?
在Msdn中,有一段話描述Delegate和Event之間的關係,其實很簡單:
聲明事件:若要在類內聲明事件,首先必須聲明該事件的委託類型。
【2】Delegate和Event配合使用的效果
看下面幾幅圖,這是我從一個C#的Application程序截下來的:
從上圖看到,在響應圖形界面的操做中,咱們用到了Event和Delegate,相信這也咱們使用Event和Delegate最頻繁的地方了。這裏我還想羅嗦一下,平時須要咱們本身寫代碼的界面事件響應函數,如:button_Click(…),其實都是回調函數,在自動生成的文件Form1.Designer.cs中,VS把事件和其對應的回調函數(即:button_Click(…)等)關聯起來,當觸發某事件時,對應的回調函數便會執行。
【3】「public Notify notifier」和「public event Notify notifier」的區別
關於這個問題,咱們直接ildasm看看IL代碼吧:>
「public Notify notifier」的IL代碼,如圖:
「public event Notify notifier」的IL代碼,如圖:
差異其實已經很明顯了,「public Notify notifier」至關於Class裏面的Field,訪問級別是public,而「public event Notify notifier」則至關於Property,訪問級別是private!因爲以上的差異,他們在某些使用上,會稍有不一樣,詳細的可參考shensr寫的《delegate vs. event》。
五.Delegate中的Invoke與BeginInvoke方法
簡單說一下,Invoke與BeginInvoke都是執行Delegate裏的搭載函數,而不一樣的是:Invoke是一個同步方法,BeginInvoke是一個異步方法。關於這個,有一篇文章《Invoke and BeginInvoke》,對此介紹的比較詳細,這裏就很少說了。
六.小結
回顧一下,到底何時咱們可能會用到Delegate:
【1】.當咱們在C#中須要相似函數指針這樣東西時。
【2】.當咱們須要使用回調函數的時候。
【3】.須要異步調用的時候。
【4】.實現觀察者模式的時候。
【5】.處理事件響應的時候。
以上內容均爲我的見解,若是有錯漏,請各位及時指出:>