談C#中的Delegate

來源:互聯網
上載者:User

標籤:des   style   blog   http   ar   io   color   os   使用   

引言

    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】.處理事件響應的時候。

    以上內容均為個人看法,如果有錯漏,請各位及時指出:>

談C#中的Delegate

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.