繼上一篇委托后,我們繼續來探討事件,因為委托和事件有著不可分割的關系。通過本文,相信你會對事件有更深刻的認識和理解,不信,你看!
用event 關鍵字使您可以聲明事件。 事件是類在相關事情發生時發出通知的方法?!竞喪觥渴录褪穷愒诎l生其關注的事情的時候用來提供通知的一種方式。
要理解事件必須要先知道一下兩個角色:
事件發行者,也稱為發送者(sender),說白了就是一個對象,這個對象會自身維護本身的狀態信息。當自身狀態信息發生變動時,就觸發一個事件,并通知所有事件的訂閱者
對事件感興趣的對象,也成為接受者(recevier),可以注冊你感興趣的事件,通常會其提供一個事件處理程序,在事件發行者觸發一個事件后,會自動觸發其代碼內容。
最經典的例子莫過于用戶向報社訂閱雜志,下面就用一張圖來說明事件的流程。
報社有各種各樣的雜志,當用戶想要查看所需雜志時,則必須首先得 訂閱 該雜志,當報社 發行 了該雜志時,則訂閱了雜志的用戶就能收到該雜志!所以這個例子中事件就是報社發行的雜志事件,同時將該雜志分發給已經訂閱該雜志的用戶手中,從而形成了一個完備的事件機制。下面我們就此例用代碼來進行實現。
1 public class Publisher /*發行者*/ 2 { 3 public delegate void Publish(); /*事件代理*/ 4 public event Publish OnPublish; /*事件*/ 5 public void Issue() /*觸發事件方法*/ 6 { 7 if (OnPublish != null) 8 { 9 Console.WriteLine("發行刊物");10 OnPublish();11 }12 }13 }14 public class Subscriber /*訂閱者*/15 {16 public void Recieve() /*訂閱者事件處理程序*/17 {18 Console.WriteLine("接受刊物");19 }20 }21 class Program22 {23 static void Main(string[] args)24 {25 Publisher pub = new Publisher();26 Subscriber user = new Subscriber();27 pub.OnPublish += new Publisher.Publish(user.Recieve); /*向事件發行者訂閱一個事件*/28 pub.Issue();29 Console.ReadKey();30 }31 }
public delegate void Publish(); 事件的代理。 Issue() 觸發事件的方法。 Recieve() 訂閱者事件處理程序??刂婆_輸出入下:
通過這一個例子想必我們對事件機制有了初步的認識。接下來為了更真實的理解事件機制,我們假設有如下情景:由于該報社混的不錯,在一小段時間內取得了不錯的業績,開始發行兩種雜志,一種是博客園雜志,另一種是博問雜志,鑒于此現將代碼進行改寫如下:
1 public class Publisher 2 { 3 public delegate void PubCnblogs(string magezineName); 4 public delegate void PubCnblogsQuestion(string magezineName); 5 public event PubCnblogs OnPubCnblogs; 6 public event PubCnblogsQuestion OnPubCnblogsQuestion; 7 public void IssueCnblogs() 8 { 9 if (OnPubCnblogs != null)10 {11 Console.WriteLine("報社發行博客園雜志");12 OnPubCnblogs("博客園");13 }14 }15 public void IssueCnblogsQuestion()16 {17 if (OnPubCnblogsQuestion != null)18 {19 Console.WriteLine("報社發行博問雜志");20 OnPubCnblogsQuestion("博文");21 }22 }23 }24 public class Subscriber25 {26 public string Name { get; set; }27 28 public Subscriber(string name)29 {30 this.Name = name;31 }32 public void Recieve(string magezineName)33 {34 Console.WriteLine(this.Name + "接受" + magezineName + "雜志");35 }36 }37 class Program38 {39 static void Main(string[] args)40 {41 Publisher pub = new Publisher();42 Subscriber xh = new Subscriber("小紅");43 pub.OnPubCnblogs += new Publisher.PubCnblogs(xh.Recieve);44 45 46 Subscriber xm = new Subscriber("小明");47 pub.OnPubCnblogs += new Publisher.PubCnblogs(xm.Recieve);48 pub.OnPubCnblogsQuestion += new Publisher.PubCnblogsQuestion(xm.Recieve);49 50 pub.IssueCnblogs();51 pub.IssueCnblogsQuestion();52 53 Console.WriteLine();54 Console.WriteLine("過了一段時間");55 pub.OnPubCnblogsQuestion -= new Publisher.PubCnblogsQuestion(xm.Recieve);56 pub.IssueCnblogs();57 pub.IssueCnblogsQuestion();58 Console.ReadKey();59 }60 61 62 }
看上述代碼報社發行了博客園雜志和博問雜志,小紅只訂閱博客園雜志,小明訂閱了博客園雜志和博問雜志,并通過 IssueCnblogs 和 IssueCnblogsQuestion 方法來進行觸發發行這兩種雜志,經過一段時間后,小明取消訂閱博問雜志,最后小紅和小明收到的都是訂閱的博客園雜志。打印如下:
綜上所述我們得出定義一個完整事件有四個步驟:
public delegate void EventNameHandler (object sender, EventArgs e)public event EventNameHandler EventName;
protected virtual void OnEventName (EventArgs e) { if (EventName != null) { EventName (this,e); } }
下面就此規范對報社發行雜志和訂閱者訂閱雜志進行改造,如下:
1 public class PubEventArgs : EventArgs /*觸發事件的類PubEventArgs*/ 2 { 3 private readonly string magezine_name; 4 private readonly DateTime magezine_time; 5 public PubEventArgs(string magezineName, DateTime magezineTime) 6 { 7 magezine_name = magezineName; 8 magezine_time = magezineTime; 9 }10 public string magezineName11 {12 get { return magezine_name; }13 }14 15 public DateTime magezineTime16 {17 get { return magezine_time; }18 }19 }20 public class Publisher /*發行者(報社即事件的發行者)*/21 {22 public delegate void PubCnblogsEventHandler(object sender, PubEventArgs e); /*發行博客園雜志的委托事件代理*/23 public delegate void PubCnblogsQuestionEventHandler(object sender, PubEventArgs e); /*發行博問雜志的委托事件代理*/24 public event PubCnblogsEventHandler PubCnblogs; /*發行博客園雜志事件*/25 public event PubCnblogsQuestionEventHandler PubCnblogsQuestion; /*發行博問雜志事件*/26 27 public virtual void OnPubCnblogs(PubEventArgs e) /*提供一個觸發發行博客園雜志事件的受保護的方法*/28 {29 PubCnblogsEventHandler cnblogs = PubCnblogs;30 if (cnblogs != null)31 {32 cnblogs(this, e);33 }34 }35 public virtual void OnPubCnblogsQuestion(PubEventArgs e) /*提供一個觸發發行博問雜志事件的受保護的方法*/36 {37 PubCnblogsQuestionEventHandler question = PubCnblogsQuestion;38 if (question != null)39 {40 question(this, e);41 }42 }43 public void IssueCnblogs(string magezine_name, DateTime magezine_time) /*觸發發行博客園雜志的方法*/44 {45 Console.WriteLine("發行" + magezine_name);46 OnPubCnblogs(new PubEventArgs(magezine_name, magezine_time));47 }48 public void IssueCnblogsQuestion(string magezine_name, DateTime magezine_time) /*觸發發行博問雜志的方法*/49 {50 Console.WriteLine("發行" + magezine_name);51 OnPubCnblogsQuestion(new PubEventArgs(magezine_name, magezine_time));52 }53 }54 public class Subscriber /*訂閱者(用戶即事件的接受者)*/55 {56 public string Name { get; set; }57 58 public Subscriber(string name)59 {60 this.Name = name;61 }62 public void Recieve(object sender, PubEventArgs e) /*訂閱者訂閱事件處理程序*/63 {64 Console.WriteLine(e.magezineTime + " " + Name + "接受到" + e.magezineName);65 }66 }67 class Program68 {69 static void Main(string[] args)70 {71 Publisher pub = new Publisher(); /*實例化發行者*/72 Subscriber xh = new Subscriber("小紅"); /*實例化訂閱者小紅*/73 pub.PubCnblogs += new Publisher.PubCnblogsEventHandler(xh.Recieve); /*小紅訂閱博客園雜志*/74 75 76 Subscriber xm = new Subscriber("小明"); /*實例化訂閱者小明*/77 pub.PubCnblogs += new Publisher.PubCnblogsEventHandler(xm.Recieve); /*小明訂閱博客園雜志*/78 pub.PubCnblogsQuestion += new Publisher.PubCnblogsQuestionEventHandler(xm.Recieve); /*小明訂閱博問雜志*/79 80 pub.IssueCnblogs("博客園雜志", DateTime.Now); /*報社發行博客園雜志(觸發博客園雜志事件)*/81 pub.IssueCnblogsQuestion("博問雜志", DateTime.Now); /*報社發行博問雜志(觸發博問雜志事件)*/82 83 Console.WriteLine();84 85 Console.WriteLine("過了一段時間");86 87 pub.PubCnblogsQuestion += new Publisher.PubCnblogsQuestionEventHandler(xh.Recieve); /*小紅覺得博客園雜志不錯,繼續訂閱博問雜志*/88 pub.PubCnblogsQuestion -= new Publisher.PubCnblogsQuestionEventHandler(xm.Recieve); /*小明覺得有點太多,看不過來,于是取消訂閱博問雜志*/89 pub.IssueCnblogs("博客園雜志", DateTime.Now); /*報社發行博客園雜志(觸發博客園雜志事件)*/90 pub.IssueCnblogsQuestion("博問雜志", DateTime.Now); /*報社發行博問雜志(觸發博問雜志事件)*/91 Console.ReadKey();92 }93 94 95 }
根據上述代碼打印出:
通過上述學習我們知道了事件和基本用法以及相關命名規范,下面我們一起來探討事件本質。
通過上述我們寫的 Publisher 發行者類,我們通過反編譯工具查看其IL代碼入下:
我們單拿發行者中的博問委托來看即可,我們上述在代碼中定義的委托變量是 public event PubCnblogsQuestionEventHandler PubCnblogsQuestion; 但此時我們發現變量的訪問權限變成了 private 私有的,同時該私有委托變量中添加了兩個方法 add 和 remove ,所以此時我們就得出這樣兩個結論,通過添加 event 關鍵字:(1)自動將委托變量變成了私有的(2)同時生成了add()和remove()兩個方法,也就說明事件中+=和-=是通過add()和remove()方法來實現的。
鑒于此我們看看add()方法到底是什么東西?
add()內部實現最終是調用了委托的最終的父類的Delegate中的Combine方法來實現的,同時我們也能看到 this.PubCnblogsQuestion ,通過上述我們知道用event關鍵字聲明的委托變量是私有的,那這個調用的PubCnblogsQuestion變量是哪來的呢?不難看出通過event關鍵字聲明中其實就自動生成了委托變量同名的變量。還不信的話,請看下圖中的操作,會讓你深信不疑。
當實例化發行者類后再對其委托變量PubCnblogsQuestion進行賦值再編譯生成會通不過,上面也有說,此時你操作的是event關鍵字會自動生成與聲明的委托變量同名的一個變量只能對其進行+=或者-=操作,而你聲明的委托變量是私有的,你是無法訪問的。通過這也驗證了這一觀點。
同理我們查看其remove()方法的實現,想必你也能猜到,肯定也是操作委托中的最終父類Delegate中的Remove()方法對其進行-=操作。眼見為實:
委托依賴事件,何以見得呢?那么 想想如果沒有事件會怎樣,想想當在實際項目中,上一個程序員用到了委托,當該程序員卷鋪蓋走人,等到下一個程序員來時不知道里面到底實現了什么或者說對其寫的委托實現進行了remove,那可怎么辦!如果有了事件的話,你根本無法去操作委托對象,只能通過+=和-=來操作,這樣就避免了委托的濫用!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。