C# 委托總結


 

一、委托

委托的本質:

委托是一種特殊的數據類型,它表示某種特定類型的函數,並且可以表示多個函數,將這些函數串聯起來。使用委托就好像函數調用一樣。

委托實質上是一個類,編譯器會根據關鍵字delegate自動生成一個從System.Delegate類派生的類。所以,它具有可訪問性,public, private等,也包含幾個默認的成員函數和屬性。(這些可通過IL代碼看出編譯器為委托生成的具體的類名稱和代碼)

 

委托的作用:

委托時一種在C#中實現函數動態調用的方式,通過委托可以將一些相同類型的函數串聯起來依次執行。委托同時還是函數回調事件機制的基礎。

在函數調用時,委托鏈上可以具有相同的函數,只要通過“+=”操作添加到委托鏈上即可。

 

委托的定義:

delegate return_type DelegateName(Type1 para1, Type2 para2, ... ,[TypeN paraN]);

delegate float DFloatFunc(int val1, float val2); // 定義一個委托類型

 

泛型委托

如果你知道泛型,那么就很容易理解泛型委托,說白了就是含有泛型參數的委托,例如:

public delegate T Calculator<T> (T arg);

我們可以把前面的例子改成泛型的例子,如下:

public delegate T Calculator<T>(T arg); class Program { static int Double(int x) { return x * 2; } static void Main(string[] args) { int[] values = { 1, 2, 3, 4 }; Utility.Calculate(values, Double); foreach (int i in values) Console.Write(i + " "); // 2 4 6 8  Console.ReadKey(); } } class Utility { public static void Calculate<T>(T[] values, Calculator<T> c) { for (int i = 0; i < values.Length; i++) values[i] = c(values[i]); } }

Func 和 Action 委托

有了泛型委托,就有了一能適用於任何返回類型和任意參數(類型和合理的個數)的通用委托,Func 和 Action。如下所示(下面的in表示參數,out表示返回結果):

delegate TResult Func <out TResult> (); delegate TResult Func <in T, out TResult> (T arg); delegate TResult Func <in T1, in T2, out TResult> (T1 arg1, T2 arg2); ... 一直到 T16 delegate void Action (); delegate void Action <in T> (T arg); delegate void Action <in T1, in T2> (T1 arg1, T2 arg2); ... 一直到 T16

有了這樣的通用委托,我們上面的Calculator泛型委托就可以刪掉了,示例就可以更簡潔了:

public static void Calculate<T>(T[] values, Func<T,T> c) { for (int i = 0; i < values.Length; i++) values[i] = c(values[i]); }

Func 和 Action 委托,除了ref參數和out參數,基本上能適用於任何泛型委托的場景,非常好用。

 

參數類型兼容

在OOP中,任何使用父類的地方均可以用子類代替,這個OOP思想對委托的參數同樣有效。如:

delegate void StringAction(string s);
class Program {
    static void Main() {
        StringAction sa = new StringAction(ActOnObject);
        sa("hello");
    }
    static void ActOnObject(object o) {
        Console.WriteLine(o); // hello

    }
}

二、事件理論基礎

當我們使用委托場景時,我們很希望有這樣兩個角色出現:廣播者和訂閱者。我們需要這兩個角色來實現訂閱和廣播這種很常見的場景。

廣播者這個角色應該有這樣的功能:包括一個委托字段,通過調用委托來發出廣播。而訂閱者應該有這樣的功能:可以通過調用 += 和 -= 來決定何時開始或停止訂閱。

事件就是描述這種場景模式的一個詞。事件是委托的一個子集,為了滿足“廣播/訂閱”模式的需求而生。

事件建立在委托機制之上,通過該機制,某個類在發生某些特定的事情之后,通知其它類或對象正在發生的事情。

 

事件(委托)

從本質上來說,事件其實就是委托,但是它通常是特定類型的的函數類型,具有以下特點:

事件發行者(類)確定何時引發事件,事件訂閱者確定如何響應該事件。

一個事件可以有多個訂閱者。一個訂閱者可以處理來自多個發行者的多個事件。

沒有訂閱者的事件,永遠不會被調用。

如果一個事件有多個訂閱戶,當引發該事件時,會同步調用多個事件處理程序。

在.NET類庫中,事件是基於EventHandle委托和EventArgs基類的。

 

事件響應函數委托

其通常沒有返回值,有sender 和 arg兩個參數。在定義一個事件之前,要先定義事件的參數類型,該類型包含了事件發起者需要提供給事件訂閱者的信息。

 

引發事件

實際上就是調用委托變量。但是在事件被定義之后,該變量默認為null, 直接引發會產生異常,所以調用之前要判斷事件是否為null(是否已經被訂閱)。通常通過定義OnXXX()的函數來引發XXX事件,在該函數中首先判斷事件是否被訂閱,如果被訂閱則引發該事件。

 

訂閱和處理事件

此方面的一個核心元素是事件響應函數。事件響應函數是符合呀哦訂閱的事件委托類型的函數,它通常根據事件的引發者和參數進行相應的處理。

由於事件的本質是委托,所以事件的訂閱實際上通過“+=”運算將當前類的事件響應函數添加到時間段額委托鏈中,在引發事件時就可以調用該處理函數。

 

委托的使用

 聲明一個事件

聲明一個事件很簡單,只需在聲明一個委托對象時加上event關鍵字就行。如下:

public delegate void PriceChangedHandler (decimal oldPrice, decimal newPrice); public class IPhone6 { public event PriceChangedHandler PriceChanged; }

 事件的使用和委托完全一樣,只是多了些約束。下面是一個簡單的事件使用例子:

public delegate void PriceChangedHandler(decimal oldPrice, decimal newPrice);

public class IPhone6 {
    decimal price;
    public event PriceChangedHandler PriceChanged;
    public decimal Price {
        get { return price; }
        set {
            if (price == value) return;
            decimal oldPrice = price;
            price = value;
            // 如果調用列表不為空,則觸發。
            if (PriceChanged != null)
                PriceChanged(oldPrice, price);
        }
    }
}

class Program {
    static void Main() {
        IPhone6 iphone6 = new IPhone6() { Price = 5288 };
        // 訂閱事件
        iphone6.PriceChanged += iphone6_PriceChanged;

        // 調整價格(事件發生)
        iphone6.Price = 3999;

        Console.ReadKey();
    }

    static void iphone6_PriceChanged(decimal oldPrice, decimal price) {
        Console.WriteLine("年終大促銷,iPhone 6 只賣 " + price + " 元, 原價 " + oldPrice + " 元,快來搶!");
    }
}

有人可能會問,如果把上面的event關鍵字拿掉,結果不是一樣的嗎,到底有何不同?

沒錯可以用事件的地方就一定可以用委托代替。

但事件有一系列規則和約束用以保證程序的安全可控,事件只有 += 和 -= 操作,這樣訂閱者只能有訂閱或取消訂閱操作,沒有權限執行其它操作。如果是委托,那么訂閱者就可以使用 = 來對委托對象重新賦值(其它訂閱者全部被取消訂閱),甚至將其設置為null,甚至訂閱者還可以直接調用委托,這些都是很危險的操作,廣播者就失去了獨享控制權。

事件保證了程序的安全性和健壯性。

 

 

 

 

參考資料

[ASP.NET MVC 大牛之路]02 - C#高級知識點概要(1) - 委托和事件

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM