[C#] Delegate, Multicase delegate, Event

聲明:這篇博客翻譯自:https://www.codeproject.com/Articles/1061085/Delegates-Multicast-delegates-and-Events-in-Csharp網絡

第一次翻譯英文博客,因爲水平(技術水平+英語理解能力)有限/不足,確定會有所疏漏/錯誤,請及時指正。架構

介紹:

在網絡上搜索一下關於C#代理,事件,多播代理的文章,不少不少。不過仍是有些地方講的不明白/透徹。這篇博客將以簡單易懂的方式來說解這3個概念。下面先回答一下:app

  • 什麼是delegate,在哪裏使用delegate?
  • 什麼是multicast delegate?
  • 什麼時候使用delegate,Event

簡要的答案:

下面的圖解解釋了delegate,multicase delegate, event之間的聯繫。函數

  • Delegate是方法的指針,能夠用做回調函數;
  • Multicast delegate用做調用多個回調函數;
  • Event封裝了delegate,而且實現了訂閱-發佈模型;
  • Event和multicase delegate都是delegate,所以delegate是event和multicase delegate的基礎;

在本篇文章的剩餘部分,咱們未來更詳細的理解上面這些話。spa

Delegate是方法指針

「Delegate是方法的指針」,你能夠經過delegate來調用指向的方法。翻譯

經過下面3步建立delegate:代理

聲明delegate,PS: 在這一步你將使用delegate關鍵字來聲明委託,注意必須保證delegate的簽名和須要指向的函數/方法簽名同樣/一致。例以下面代碼中「SomeMethod()"無返回值且無輸入參數。所以示例代碼中的"SomeMethodPtr()"delegate定義是合適的;指針

建立一個delegate對象,PS: 當你建立了一個delegate以後,須要建立一個delegate的對象才能使用它,參考代碼中註釋Step2;code

調用delegate,PS: 經過調用delegate的Invoke方法調用"SomeMethod".orm

    delegate void SomeMethodPtr();  // 1. Declare delegate

    static void Main(string[] args)
    {
        SomeMethodPtr ptrObject = SomeMethod;   // 2. Create object of delegate
        ptrObject.Invoke(); // 3. Invoke the delegate
    }

    static void SomeMethod()
    {
        // some code
    }

爲何要使用這種看法調用呢?

目前咱們理解了delegate是方法/函數的指針。經過delegate間接調用一個方法有什麼好處呢?

當咱們須要調用的code在另一個程序集中,例以下面的文件搜索類在一個其餘assebmly中。PS:這個文件搜索代碼只是一個模擬代碼。

public class SearchFile
{
    public void Search()
    {
        // File search is happening
        for(int i=0;i<100;i++)
        {
            string str = "File " + i;
        }
    }
}

如今咱們在另一個單獨的Console應用中調用上面的代碼,當搜索完畢後,馬上通知UI並顯示搜索的文件名。

static void Main(string[] args)
{
    SearchFile fl = new SearchFile();
    fl.Search();
}

換句話所咱們須要一個CALLBACK(回調函數)當File Search結束後通知Console程序。這就是爲何說delegate是用來作回調函數的。

不少開發者想爲何不在Searh()方法中直接調用「Console.Write」進行輸出呢。這樣作會將UI技術和核心代碼有耦合。若是咱們把這段代碼給WPF/WinForms程序調用,Console.Write作UI輸出不適用.

    public class SearchFile
    {
        public void Search()
        {
            // File search is happening
            for (int i = 0; i < 100; i++)
            {
                string str = "File" + i;
                Console.Write(str);
            }
        }
    }

使用delegate實現一個回調函數
在SearchFile類中實現delegate,第一件事是暴露一個delegate的調用。在下面的SearchFile類中,定義一個WheretoCall的delegate,仔細觀察Step1,2中的代碼,此時的WheretoCall是空的,任何一個客戶端程序想要有一個回調事件,傳遞一個簽名一致的方法便可。

    public class SearchFile
    {
        public delegate void WheretoCall(string status);    // Step1

        public WheretoCall wheretoCall = null;      // Step 2

        public void Search()
        {
            // File search is happening
            for (int i = 0; i < 100; i++)
            {
                string str = "File" + i;
                wheretoCall(str);       // Step 3
            }
        }
    }

如今調用上述代碼時只須要傳遞一個方法的引用到delegate指針便可。請看下面代碼中Step 1

    static void Main(string[] args)
    {
        SearchFile fl = new SearchFile();
        fl.wheretoCall = CallHere;  // Step 1
        fl.Search();
    }

    static void CallHere(string message)
    {
        Console.Write(message);
    }

Multicast delegate(多播委託)

上面圖示中SearchFile"類發送通知(廣播)到3個訂閱方法。咱們能夠把這種模式稱爲訂閱發佈架構。SearchFile類是發佈者。

    static void CallHereToWriteToFile(string message)
    {
        System.IO.File.WriteAllText(@"c:\somtext.txt", message);
    }
    static void CallHereForConsole(string message)
    {
        Console.WriteLine(message);
    }
    static void CallHereToWriteInternally(string message)
    {
        messages.Add(message);
    }

爲了實現上述目標,咱們不須要修改SearchFile類,在客戶端調用時使用「+=」將方法分配給「wheretoCall」代理。若是你不想訂閱,使用"-="取消訂閱便可。

    static void Main(string[] args)
    {
        SearchFile fl = new SearchFile();
        fl.wheretoCall += CallHereForConsole;
        fl.wheretoCall += CallHereToWriteToFile;
        fl.wheretoCall += CallHereToWriteInternally;
        fl.Search();
    }

Event -- 封裝後的delegate

經過multicast delegate實現的訂閱-發佈者模式有一個嚴重的問題,訂閱者能夠修改這個delegate。在一個真實的訂閱-發佈者模式/廣播模式中,訂閱者只能訂閱/取消訂閱。

下面的代碼中,訂閱者能夠將發佈者delegete設置爲NULL,而且能夠自由調用delegate。

    static void Main(string[] args)
    {
        SearchFile f1 = new SearchFile();
        f1.wheretoCall += CallHereForConsole;
        f1.wheretoCall.Invoke("status"); // The client can invoke.
        f1.wheretoCall = null; // The delegate can be modified.
        f1.wheretoCall += CallHereToWriteInternally;
        f1.Search();
    }

下面使用event關鍵字封裝delegate,

    public class SearchFile
    {
        public delegate void WheretoCall(string status);    // Step1

        public event WheretoCall wheretoCall = null;      // Step 2

        public void Search()
        {
            // File search is happening
            for (int i = 0; i < 100; i++)
            {
                string str = "File" + i;
                wheretoCall(str);       // Step 3
            }
        }
    }

編譯客戶端代碼,出現以下錯誤:

這個錯誤的意思是對於一個Event事件你只能訂閱(+=)或者取消訂閱(-=)。

總結:

相關文章
相關標籤/搜索