聲明:這篇博客翻譯自:https://www.codeproject.com/Articles/1061085/Delegates-Multicast-delegates-and-Events-in-Csharp網絡
第一次翻譯英文博客,因爲水平(技術水平+英語理解能力)有限/不足,確定會有所疏漏/錯誤,請及時指正。架構
在網絡上搜索一下關於C#代理,事件,多播代理的文章,不少不少。不過仍是有些地方講的不明白/透徹。這篇博客將以簡單易懂的方式來說解這3個概念。下面先回答一下:app
下面的圖解解釋了delegate,multicase delegate, event之間的聯繫。函數
在本篇文章的剩餘部分,咱們未來更詳細的理解上面這些話。spa
「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); }
上面圖示中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(); }
經過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事件你只能訂閱(+=)或者取消訂閱(-=)。