日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

再谈C#中的委托和事件

發布時間:2023/12/4 C# 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 再谈C#中的委托和事件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

寫在最前

我相信全網關于委托和事件的文章和概述,大家應該已經讀過很多篇。但是就我的觀察來看,大多數文在講述這方面概念時,都會用燒開水和狗叫主人的例子來講述事件怎么工作,這樣比喻固然與生活聯系緊密,但看多了難免有一些審美疲勞。

所以今天,我打算結合自己的一些工作經歷,再來談談我個人對委托和事件的理解,希望能帶給大家一些不一樣的見解。

先談概念

委托:一種引用類型,表示具有特定參數列表和返回類型的方法的引用。在實例化委托時,你可以將其實例與任何具有兼容簽名和返回類型的方法相關聯。你可以通過委托實例調用方法。委托用于將方法作為參數傳遞給其他方法。事件處理程序就是通過委托調用的方法。

事件:類或對象可以通過事件向其他類或對象通知發生的相關事情。發送(或引發)事件的類稱為“發布者”,接收(或處理)事件的類稱為“訂閱者”。

以上概述來自MSDN官方文檔:

https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/delegates/

從概念中我們其實已經可以看出,委托主要是對方法的一種引用,而事件則充當了多個類或者對象進行相互通知的橋梁。

如果我這么解釋你可以明白的話,那么我們今天的主題就已經明朗了,下面我們就用具體的代碼實例來講述。

再看例子

委托

我們需要先聲明一個委托實例,在C#中,顯示聲明自定義委托采用delegate關鍵字,聲明方式與聲明普通方法相同,需要指定訪問范圍和返回類型,同時包含訪問參數。

同時我們針對委托,聲明對應的方法,方法的返回值和參數需要與委托保持一致,若不一致則會在委托傳遞方法時出現編譯錯誤。

委托執行內部傳遞方法的方式是使用Invoke方法,此處需注意,C#中同時提供了BeginInvoke和EndInvoke的方法對,用于異步執行內部的方法,具體含義和用法可參考我之前的一篇文章:

淺談.Net異步編程的前世今生----APM篇

下面我們一起來看一下示例:

using System;namespace DelegateAndEvent {class Program{public delegate void DelegateWithNoParams();public delegate int DelegateSum(int a, int b);static void Main(string[] args){DelegateWithNoParams delegate1 = new DelegateWithNoParams(FunctionWithNoParams);delegate1.Invoke();DelegateSum delegate2 = new DelegateSum(FunctionSum);int c = delegate2.Invoke(10, 20);Console.WriteLine("帶返回值和參數的方法,結果為:" + c);Console.Read();}public static void FunctionWithNoParams(){Console.WriteLine("無返回值無參數的方法");}public static int FunctionSum(int a, int b){int c = a + b;return c;}} }

在此示例中,我們分別定義了一個無參數無返回值的委托和一個包含2個參數并返回int類型的委托,分別用于執行兩種對應的方法。在兩個委托執行對應的Invoke方法之后,會產生以下的結果:

結果和我們預期一致,程序同步順序地執行了兩個委托并打印出相應的結果。但是看到這里也許你會有一個疑問,既然委托執行時的結果與直接調用方法一致,那么我們為什么還需要使用委托來執行方法呢?

這時我們就要回到最初的定義:委托用于將方法作為參數傳遞給其他方法

由于實例化的委托是一個對象,因此可以作為參數傳遞或分配給一個屬性。這允許方法接受委托作為參數并在稍后調用委托。這被稱為異步回調,是在長進程完成時通知調用方的常用方法。當以這種方式使用委托時,使用委托的代碼不需要知道要使用的實現方法。功能類似于封裝接口提供的功能。

我們一起使用一個比較直觀的例子來驗證:

using System;namespace ConsoleApp1 {class Program{public delegate void Del(string message);static void Main(string[] args){Del handler = new Del(DelegateMethod);MethodWithCallback(1,2,handler);Console.Read();}public static void DelegateMethod(string message){Console.WriteLine(message);}public static void MethodWithCallback(int param1, int param2, Del callback){callback(string.Format("當前的值為:{0}", (param1 + param2)));}} }

在這段代碼中,我們聲明了一個無返回值委托Del,用于接收傳入的消息,并且該委托指向了一個調用控制臺的方法DelegateMethod。而后續我們調用MethodWithCallback方法時,無需調用控制臺相關方法,而是直接將Del委托的實例作為參數傳入,就實現DelegateMethod方法的調用。這個實例就是我們上述提到的異步回調和委托對方法的引用。

運行結果如下:

此處我使用了JetBrains出品的IDE Rider,因此截圖界面會與VS有所不同,喜歡輕量級IDE的同學可以試試這款,有30天的免費試用期,地址:Rider: The Cross-Platform .NET IDE from JetBrains,此處不再過多講解。

根據我們上述的實例講解,大家對委托的作用及使用場景應該有了初步的理解,但是我們仔細想一想,上述的場景似乎少了些什么,為什么我們的委托始終只能指向一個方法進行調用呢?

這里就要引出我們接下來的概念:多播委托。

事實上,委托是可以調用多個方法的,這種方式就叫做多播委托,在C#中,我們可以使用+=的運算符,將其他委托附加到當前委托之后,就可以實現多播委托,相關示例如下:

using System;namespace ConsoleApp1 {class Program{public delegate void Del();static void Main(string[] args){Del handler = new Del(DelegateMethod1);Del handlerNew = new Del(DelegateMethod2);handler += handlerNew;handler.Invoke();Console.Read();}public static void DelegateMethod1(){Console.WriteLine("天才第一步!");}public static void DelegateMethod2(){Console.WriteLine("天才第二步!");}} }

在這個示例中,我們重新編寫了一個方法叫DelegateMethod2,同時我們又聲明了一個新的委托對象handlerNew指向該方法。接著我們使用+=的方式將handlerNew添加至handler并執行該委托,得到的結果如下:

如我們先前所料,多播委托把多個方法依次進行了執行。此時如果某個方法發生異常,則不會調用列表中的后續方法,如果委托具有返回值和/或輸出參數,它將返回上次調用方法的返回值和參數。與增加方法相對應,若要刪除調用列表的方法,則可以使用-=運算符進行操作。

關于委托的理解與常用方式,我們就講解到這里,事實上,多播委托常用于事件處理中,由此可見,事件與委托有著千絲萬縷的聯系,下面我們就拉開事件的序幕。

事件

如前文講解時所說,事件是一種通知行為,因此要分為事件發布者和事件訂閱者。而且在.Net中,事件基于EventHandler委托和EventArgs基類的,因此我們在聲明事件時,需要先定義一個委托類型,然后使用event關鍵字進行事件的定義。

相關的示例如下:

using System;namespace ConsoleApp1 {public class PublishEvent{public delegate void NoticeHandler(string message);public event NoticeHandler NoticeEvent;public void Works(){//觸發事件OnNoticed();}protected virtual void OnNoticed(){if (NoticeEvent != null){//傳遞事件及參數NoticeEvent("Notice發布的報警信息!");}}}public class SubscribEvent{public SubscribEvent(PublishEvent pub){//訂閱事件pub.NoticeEvent += PrintResult;}/// <summary>/// 訂閱事件后的響應函數/// </summary>/// <param name="message"></param>void PrintResult(string message){Console.WriteLine(string.Format("已收到{0}!采取措施!",message));}}class Program{static void Main(string[] args){PublishEvent publish = new PublishEvent();SubscribEvent subscrib = new SubscribEvent(publish);//觸發事件publish.Works();Console.Read();}} }

從事例中我們可以看出,我們分別定義了發布者和訂閱者相關的類。

在發布者中,我們需要聲明一個委托NoticeHandler,然后定義一個此類型的事件NoticeEvent。在定義對象之后,我們需要對事件進行執行,因此有了OnNoticed方法,此方法只用于事件本身的執行。那么什么時候才能執行事件呢?于是我們又有了觸發該事件的方法Works,當Works方法被調用時,就會觸發NoticeEvent事件。

而在訂閱者中,我們需要對NoticeEvent事件進行訂閱,因此我們需要發布者的對象PublishEvent,同時需要對它的事件進行訂閱。正如我們前文所說,訂閱使用+=的方式,與多播委托的使用是一致的,而+=后的對象正是我們需要響應后續處理的方法PrintResult。當事件被觸發時,訂閱者會接收到該事件,并自動執行響應函數PrintResult。

執行結果如下圖所示:

從執行結果中我們可以看出,在事件被觸發后,訂閱者成功接收到了發布者發布的事件內容,并進行自動響應,而我們在此過程中從未顯式調用訂閱者的任何方法,這也是事件模型的本質意義:從發布到訂閱。

在微軟官方文檔中提到,事件是一種特殊的多播委托,只能從聲明它的類中進行調用。客戶端代碼通過提供對應在引發事件時調用的方法的引用來訂閱事件。這些方法通過事件訪問器添加到委托的調用列表中,這也是我們可以使用+=去訂閱事件的原因所在,而取消事件則和多播委托一致,使用-=的方式。

關于事件的使用場景還有一種與多線程通知相關的典型用法,具體含義和用法可參考我之前的另一篇文章:

淺談.Net異步編程的前世今生----EAP篇

最后總結

本文我們講解了委托和事件之間的關系,以及它們的使用場景。在我個人的工作經歷中,曾經有3年左右的時間從事C/S相關的開發工作,其中包含了大量多線程、委托和事件的使用場景。主要用于在開發WinForm程序時,不同窗體(包含父子窗體)之間進行相互通信,其中都是基于事件的發布和訂閱作為實現的。而委托的使用場景則更多,很多C#方法在使用時都會傳入一個Action或者Func委托作為參數,而這些參數同時又支持Lambda表達式,這就又引出了匿名函數的概念,由于篇幅所限,此處不再進行講解,大家可以自行搜索資料進行了解和學習。

除了具體的技術點之外,在我們的設計模式中也有事件的使用身影,最典型的莫過于觀察者模式。關于觀察者模式,網上眾說紛紜,也有很多資料會將它與發布-訂閱模式混為一談。而實際上這兩種模式并不完全是同一種概念和實現方式,那么下一次我們將會從設計模式著手,談一談觀察者模式和發布-訂閱模式的異同,敬請期待!

您的點贊和在看是我創作的最大動力,感謝支持

公眾號:wacky的碎碎念

知乎:wacky

總結

以上是生活随笔為你收集整理的再谈C#中的委托和事件的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。