C#委托和事件的概念
一、事件的本質
事件是軟件系統里的兩個子系統之間,或者兩個模塊之間,或者兩個對象之間發送消息,并處理消息的過程。在面向對象的世界里,就可以統一認為是兩個對象之間的行為。
兩個對象之間發送的這種消息,對發送方來講是產生一個事件,對接受方來講是需要處理某個事件。這種消息可以是用戶操作產生的或者軟件系統里的某個對象產生的。
???????????????????????????????????????????????????????????????????????????????????????? ?對象之間的事件處理
?
從上圖可見,對象一產生一個事件,這個事件發生以后需要對象二執行某種動作。這就是事件機制。對象一是事件的產生者,或者發送者;對象二是事件的接收者或者訂閱者。對象一產生某種消息,需要對象二響應并處理這給消息,這就是事件的本質。
以往的很多軟件系統都在采用事件機制處理很多問題。例如從最本質的計算機體系中的軟中斷處理,到masm中的jump,到c/c++中的回調函數等等。只不過越高級的軟件系統處理事件或者其提供的很多處理方法越接近人的思維,而越遠離機器思維。構建軟件系統的方法從本質上就是從機器思維走向人的思維的過程。
二、事件機制的好處
1、直接調用
采用事件機制有什么好處?事件發送者為什么不直接調用事件接受者提供的處理函數呢?
?????????????????????????????????????????????????????????????????????????????????????????????????調用機制
?
如果所示,兩個對象之間的調用機制。對象B調用對象A的方法,可以通過函數指針或者跳轉(匯編語言)等實現。這種方法造成的結果是A和B的緊密耦合,即B對A有很強的依賴性。可以看成B是事件的發布者,A是事件的響應和處理者。不過這種機制用事件機制解釋從理論上就比較牽強了。同一種事物,其實現的思想不一樣。
現在假設有個對象C也要響應B的事件。那么,按照上面的這種機制,需求修改對象B的代碼,調用對象C的方法。這樣機制造成了非常強的依賴關系。代碼的修改和擴展非常麻煩。如果對象越多,這種關系越多,整個系統越復雜。如果一個系統里面對象很多,這種依賴關系也很多的情況下,這種調用關系就會十分復雜,對系統的健壯性和優良性會造成影響。
2、回調機制
如果按照c#的委托思想,B需要事先提供對事件處理函數的某些回調指針。這樣,其它對象,例如A和C就去修改它的回調指針,把自己的方法聯系到上面。但是它們之間的耦合關系就比上面簡單了。
?????????????????????????????????????????????????????????????????????????????????????????????????回調機制
?
回調機制的思想已經比較接近委托的概念。其實委托在本質上也就和回調指針差不多,只是概念上更加高級。對象B作為事件的發布者,事先定義一些回調函數指針,然后在本地合適的地方調用這些指針指向的函數。而事件訂閱者或者處理者A和C所作的就是讓給這些空指針賦值,把自己的事件處理方法賦給它,從而實現B調用A和C的方法。
在?C?或?C++?中與委托最為相似的是函數指針。然而,函數指針只能引用靜態函數,而委托可以引用靜態方法和實例方法。當委托引用實例方法時,委托不僅存儲對方法入口點的引用,還存儲對為其調用該方法的類實例的引用。與函數指針不同,委托是面向對象、類型安全并且安全的。
三、事件機制的實現
1、委托的局限
如果單純用委托,對于事件的發布者B來說,假設它發布事件e,對于事件e,它目前已經知道有A和C對象需要訂閱這個事件。所以,它就申明兩個委托對象引用(本質上類似于函數指針),然后讓A和C對象來采用類似回調的機制訂閱和響應事件。
如果后來,有個對象D也需要訂閱B的事件e,它怎么辦呢?一種情況是D修改B的一個委托對象引用,把自己的處理方法包裝成一個委托對象付給它。這樣,D就搶奪了A或者C的訂閱。否則,就需要修改B的代碼,添加一個類似的委托對象引用,以便讓D來使用。
這樣做的后果是事件發布者B需要申明很多委托對象的引用變量。結果是弄得代碼維護比較混亂,并且使用者也很多,依賴關系也不容易搞清楚,容易發生錯誤。
2、事件的引入
有了委托,就提供了類似回調一樣的功能。但是,回調機制需要事件發布者和事件訂閱者雙方的共同參與和努力。也就是,每增加一個訂閱者,那么發布者對象就需要提供一個委托引用,讓訂閱者掛鉤。
如果事件的發布者發布一個事件以后就不在關心誰來訂閱它,那么以后的處理就交給了使用者,而發布者不再關心事件處理者的問題。
????????????????????????????????????????????????????????????????????????????????????????????????訂閱機制
?
C#事件的事件就是這種訂閱機制,真正的訂閱。發布者不需要關心訂閱者。
C#事件給訂閱者提供了對事件響應的注冊和反注冊功能。訂閱和撤銷完全是事件接受方的行為。
C#事件機制的實現包括以下幾步:
1、?事件發布者定義一個委托類型;
2、?事件發布者定義一個事件,并且關聯到已經定義的委托上。
3、?事件訂閱者需要產生一個委托實例,并把它添加到委托列表。
所以,事件event可以看成是一個事件列表,訂閱者可以注冊和撤銷自己的響應和處理機制,但是它沒有辦法更改整個列表(原則上)。所以,提供了更強、更安全的方式。
四、事件機制的代碼實例
??????????????????????????????????????????????????????????????????????????????????????????應用程序結構圖
?
如圖所示,事件發布對象發布一個事件;事件訂閱對象訂閱和處理該事件。
using?System;
namespace?EventExample
{
????///<summary>
????///?MainClass :?主應用程序類
????///</summary>
????class?MainClass
??? {
???????///<summary>
???????///應用程序的主入口點。
???????///</summary>
?????? [STAThread]
???????static?void?Main(string[] args)
?????? {
?????????? EventPublisher publisher =?new?EventPublisher();
?????????? EventReader1 reader1 =?new?EventReader1(publisher);
?????????? EventReader2 reader2 =?new?EventReader2(publisher);
?????????? publisher.DoSomthing();
?????????? Console.WriteLine("This program already finished!");
?????????? Console.ReadLine();
?????? }
??? }
????///<summary>
????///?EventPublisher :?事件的發布者。
????///</summary>
????public?class?EventPublisher
??? {
???????//?第一步是申明委托
???????public?delegate?int?sampleEventDelegate(string?messageInfo);
???????//?第二步是申明與上述委托相關的事件
???????public?event?sampleEventDelegate sampleEvent;
???????public?EventPublisher()
?????? {
?????? }
???????public?void?DoSomthing()
?????? {
???????????/* ... */
???????????//?激發事件
???????????if(this.sampleEvent !=?null)
?????????? {
??????????????this.sampleEvent("hello world!");
?????????? }
???????????/* ... */
?????? }
??? }
????///<summary>
????///?EventReader1 :?事件的訂閱者1。
????///</summary>
????public?class?EventReader1
??? {
???????public?EventReader1(EventPublisher publisher)
?????? {
?????????? publisher.sampleEvent +=
??????????????new?EventExample.EventPublisher.sampleEventDelegate(ResponseEvent);
?????? }
???????private?int?ResponseEvent(string?msg)
?????? {
?????????? Console.WriteLine(msg + "?--- This is from reader1");
???????????return?0;
?????? }
??? }
????///<summary>
????///?EventReader2 :?事件的訂閱者2。
????///</summary>
????public?class?EventReader2
??? {
???????public?EventReader2(EventPublisher publisher)
?????? {
?????????? publisher.sampleEvent +=
??????????????new?EventExample.EventPublisher.sampleEventDelegate(ResponseEvent);
publisher.sampleEvent +=
??????????????new?EventExample.EventPublisher.sampleEventDelegate(ResponseEvent);
?????? }
???????private?int?ResponseEvent(string?msg)
?????? {
?????????? Console.WriteLine(msg + "?--- This is from reader2");
?????????? Console.WriteLine("Please:down enter key!");
?????????? Console.ReadLine();
?????????? Console.WriteLine("ok");
???????????return?0;
?????? }
??? }
}
??????????????????????????????????????????????????????????????????????????????????????????程序運行結果
?
總結:事件發布者發布的事件在實質上可以看成對外提供的回調函數指針列表。這個列表的容量可以動態增長。事件訂閱者可以把自己的事件注冊到這個列表或者撤銷注冊,但是它從原則上無法更改或者對其它訂閱者的注冊產生影響。事件發布者通過兩種手段使得訂閱者正確地使用事件機制:一是定義一種delegate委托類型,事件訂閱者只能按照這種類型定義事件的處理方法;二是定義與這個委托相關的event對象,使得訂閱者只負責注冊和撤銷自己的處理過程而不能隨意對別人的處理過程產生影響。
從運行結果和reader2對象把同一個處理方法注冊了兩次的前提可以看到,對于一個事件,同一個訂閱者可以把同一個處理過程注冊多次,而這個方法最終也會被執行多次。
執行事件訂閱列表中方法的順序不能被保證;而且,在這里采用的是同步調用方法,只有一個響應函數執行完畢,其它函數才會被執行。如果要方法不被阻塞(包括這里的等待用戶輸入等),就需要采用異步調用方式。
總結
以上是生活随笔為你收集整理的C#委托和事件的概念的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python玛丽冒险超级游戏程序源码
- 下一篇: C# JSON、XML互转