引言
Delegate是Dotnet1.0的時候已經存在的特性了,但由於在實際工作中一直沒有機會使用Delegate這個特性,所以一直沒有對它作整理。這兩天,我再度翻閱了一些關於Delegate的資料,並開始正式整理這個C#中著名的特性。本文將由淺入深的談一下Delegate這個特性。
一.Delegate是什麼?
Delegate中文翻譯爲“委託”。Msdn中對Delegate的解釋如下:
C#中的委託類似於C或C++中的函數指針。使用委託使程序員可以將方法引用封裝在委託對象內。然後可以將該委託對象傳遞給可調用所引用方法的代碼,而不必在編譯時知道將調用哪個方法。與C或C++中的函數指針不同,委託是面向對象、類型安全的,並且是安全的。
如果你是第一次接觸Delegate這個概念,你可能會對上面這段文字感覺不知所云,不過不要緊,你可以先把Delegate認爲就是一個函數指針。
而當你面對一個虛無的概念時,最好的應對方法就是直接看實例。下面一個簡單的Delegate使用例子。
class Program { static void OtherClassMethod(){ Console.WriteLine("Delegate an other class's method"); } static void Main(string[] args) { var 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】.處理事件響應的時候。
以上內容均爲個人看法,如果有錯漏,請各位及時指出:>
轉載請說明出處,謝謝![hyddd(http://www.cnblogs.com/hyddd/)]
參考資料
【3】《對.net事件的看法》
【5】《delegate vs. event》
【6】《C#事件(event)解析》
【7】《C# Delegate 簡介》