不少程序都有一個共同的需求,既當一個特定的程序事件發生時,程序的其餘部分能夠獲得該事件已經發生的通知。
發佈者/訂閱者模式(publisher/subscriber pattern)能夠知足這種需求。html
前一章介紹了委託。事件的不少部分都與委託相似。實際上,事件就像專門用於特殊用途的簡單委託。事件包含了一個私有的委託。
有關事件的私有委託須要瞭解的重要事項以下:編程
須要在事件中使用的代碼有5部分。併發
發佈者類必須提供事件對象。建立事件比較簡單–只須要委託類型和名字。事件聲明的語法以下代碼所示。
代碼中聲明瞭CountADozen事件。框架
class Incrementer { 關鍵字 委託類型 事件名 ↓ ↓ ↓ public event EventHandler CountedADozen; ... }
能夠經過使用逗號分隔同時聲明多個事件。異步
public event EventHandler MyEvent1,MyEvent2,OtherEvent;
還可使用static關鍵字讓事件變成靜態函數
public static event EventHandler CountedADozen;
事件是成員
一個常見誤解是把事件認爲是類型。和方法、屬性同樣,事件是類或結構的成員,這一點引出幾個重要特性。this
事件聲明須要委託類型的名字,咱們能夠聲明一個委託類型或使用已存在的。若是咱們聲明一個委託類型,它必須指定事件保存的方法的簽名和返回類型。
BCL(Base Class Library,基類庫)聲明瞭一個叫作EventHandler的委託,專門用於系統事件。設計
例:爲CountedADozen事件增長3個方法。3d
incrementer.CountedADozen+=IncrementDozensCount;//實例方法 incrementer.CountedADozen+=ClassB.CounterHandlerB;//靜態方法 mc.CountedADozen+=new EventHandler(cc.CounterHandlerC);//委託形式
例:Lambda表達式和匿名方法code
incrementer.CountedADozen+=()=>DozensCount++;//Lambda表達式 incrementer.CountedADozen+=delegate{DozensCount++;};//匿名方法
事件成員自己只保存了須要被調用的事件處理程序。若是事件沒觸發,什麼都不會發生。咱們須要確保在合適的時候有代碼來作這件事情。
例:下面代碼觸發了CountedADozen事件。
if(CountedADozen!=null) { CountedADozen(source,args); }
把事件聲明和觸發事件的代碼放在一塊兒便有了以下的發佈者類聲明。
下面展現了整個程序,代碼須要注意的地方以下:
delegate void Handler(); //聲明委託 //發佈者 class Incrementer { public event Handler CountedADozen;//建立事件併發布 void DoCount(object source,EventArgs args) { for(int i=1;i<100;i++) { if((i%12==0)&&(CountedADozen!=null)) { CountedADozen(source,args); } } } } //訂閱者 class Dozens { public int DozensCount{get;private set;} public Dozens(Incrementer incrementer) { DozensCount=0; incrementer.CountedADozen+=IncrementDozensCount;//訂閱事件 } void IncrementDozensCount()//聲明事件處理程序 { DozensCount++; } } class Program { static void Main() { var incrementer=new Incrementer(); var dozensCounter=new Dozens(incrementer); incrementer.DoCount(); Console.WriteLine("Number of dozens = {0}",dozensCounter.DozensCount); } }
GUI編程是事件驅動的,即程序運行時,它能夠在任什麼時候候被事件打斷,好比鼠標點擊,按下按鍵或系統定時器。在這些狀況發生時,程序須要處理事件而後繼續其餘事情。
程序事件的異步處理是使用C#事件的絕佳場景。Windows GUI編程普遍的使用事件,對於事件的使用,.NET框架提供了一個標準模式。事件使用的標準模式根本就是System命名空間聲明的EventHandler委託類型。EventHandler委託類型的聲明代碼以下。
public delegate void EventHandler(object sender,EventArgs e);
EventHandler委託類型的第二個參數是EventArgs類的UI項,它聲明在System命名空間中。既然第二個參數用於傳遞數據,你可能會誤認爲EventArgs類的對象應該能夠保存一些類型的數據。
例:Incrementer+EventHandler
public delegate void EventHandler(object sender,EventArgs e); //發佈者 class Incrementer { public event EventHandler CountedADozen;//建立事件併發布 public void DoCount() { for(int i=1;i<100;i++) { if((i%12==0)&&(CountedADozen!=null)) { CountedADozen(this,null); } } } } //訂閱者 class Dozens { public int DozensCount{get;private set;} public Dozens(Incrementer incrementer) { DozensCount=0; incrementer.CountedADozen+=IncrementDozensCount;//訂閱事件 } void IncrementDozensCount(object source,EventArgs e)//聲明事件處理程序 { DozensCount++; } } class Program { static void Main() { var incrementer=new Incrementer(); var dozensCounter=new Dozens(incrementer); incrementer.DoCount(); Console.WriteLine("Number of dozens = {0}",dozensCounter.DozensCount); } }
爲了向EventArgs傳入數據,而且符合標準慣例,咱們須要聲明一個派生自EventArgs的自定義類,用於保存須要傳入的數據。類的名稱應該以EventArgs結尾。
例:聲明自定義的EventArgs類,它將字符串存儲在IterationCount字段中。
public class IncrementerEventArgs:EventArgs { public int IterationCount{get;set;} }
除了自定義類外,你還須要一個使用自定義類的委託類型。要獲取該類,可使用泛型(第17章)版本的委託EventHandler<>。要使用泛型委託,須要作到如下兩點:
例:使用了自定義類和自定義委託的事件示例
public class IncrementerEventArgs:EventArgs { public int IterationCount{get;set;} } //發佈者 class Incrementer { 使用自定義類的泛型委託 ↓ public event EventHandler<IncrementerEventArgs> CountedADozen;//建立事件併發布 public void DoCount() { IncrementerEventArgs args=new IncrementerEventArgs(); for(int i=1;i<100;i++) { if((i%12==0)&&(CountedADozen!=null)) { args.IterationCount=i; CountedADozen(this,args); } } } } //訂閱者 class Dozens { public int DozensCount{get;private set;} public Dozens(Incrementer incrementer) { DozensCount=0; incrementer.CountedADozen+=IncrementDozensCount;//訂閱事件 } void IncrementDozensCount(object source,IncrementerEventArgs e)//聲明事件處理程序 { Console.WriteLine("Incremented at iteration: {0} in {1}",e.IterationCount,source.ToString()); DozensCount++; } } class Program { static void Main() { var incrementer=new Incrementer(); var dozensCounter=new Dozens(incrementer); incrementer.DoCount(); Console.WriteLine("Number of dozens = {0}",dozensCounter.DozensCount); } }
用完事件處理程序後,可使用-=運算符把事件處理程序從事件中移除。
例:移除事件處理程序示例
class Publisher { public event EventHandler SimpleEvent; public void RaiseTheEvent(){SimpleEvent(this,null);} } class Subscriber { public void MethodA(object o,EventArgs e) { Console.WriteLine("AAA"); } public void MethodB(object o,EventArgs e) { Console.WriteLine("BBB"); } } class Program { static void Main() { var p=new Publisher(); var s=new Subscriber(); p.SimpleEvent+=s.MethodA; p.SimpleEvent+=s.MethodB; p.RaiseTheEvent(); Console.WriteLine("\r\nRemove MethodB"); p.SimpleEvent-=s.MethodB; p.RaiseTheEvent(); } }
若是一個處理程序向事件註冊了屢次,那麼移除程序時,將只移除列表中該處理程序的最後一個實例。
以前我提到+=和-=運算符是事件容許的惟一運算符。看到這裏咱們應該知道,這些運算符有預約義行爲。
咱們能夠修改這些運算符的行爲,而且使用它們時可讓事件執行任何咱們但願的自定義代碼。但這是高級主題,此處只作簡單介紹。
要改變這兩個運算符的操做,能夠爲事件定義事件訪問器。
例:具備訪問器的事件聲明。兩個訪問器都有隱式值參數value,它接受實例或靜態方法的引用。
public event EventHandler CountedADozen { add { ... //執行+=運算符的代碼 } remove { ... //執行-=運算符的代碼 } }
聲明事件訪問器後,事件不包含任何內嵌委託對象。咱們必須實現本身的機制來存儲和移除事件註冊方法。
事件訪問器表現爲void方法,即沒有返回值。
from: http://www.cnblogs.com/moonache/p/6340222.html