標籤:winform class blog code http tar
事件,我相信開始學C#的朋友都會用過,在C#中很常見,比如點擊一個按鈕,上傳一張圖片等等,在WinForm或WebForm中都在使用著事件。今天,趁著有少少事件,我決定來重溫一下之前被自己略過的東西 - 事件。
好記得在之前,在用一個方法的時候,如果參數裡面有個Handler,就好害怕,其實事件還是用委託來做中介的,在事件上兩次轉到定義就去到委託了,將委託複製出來,去掉delegate就是方法簽名了,寫上自己要實現的代碼給事件賦值就OK了。
一、什麼是事件
事件涉及兩類角色:事件的發行者和事件的訂閱者。觸發事件的對象稱為事件發行者,捕獲時間並對其作出響應的對象叫做事件訂閱者。
二、事件和委託的關係
在事件觸發以後,事件發行者需要發布訊息,通知事件訂閱者進行事件處理,但事件發行者並不知道要通知哪些事件訂閱者,這就需要在發行者和訂閱者之間存在一個中介,這個中介就是委託。我們知道,委託都有一個調用列表,那麼,只需要事件發行者有這樣一個委託,各個事件訂閱者將自己的事件處理常式都加入到該委託的調用列表中,那麼事件觸發時,發行者只需要調用委託即可觸發訂閱者的事件處理常式。
三、如何聲明事件
聲明事件的文法和定義一個類的成員非常相似,也非常簡單。事實上,事件就是類成員的一種,只是事件定義中包含一種特殊的關鍵字:event。
事件的聲明有兩種方式:
1、採用自訂委託類型。
2、採用EventHandler預定義委託類型。
這兩種方式基本相同,只不過第二種是.Net Framework中普遍採用的一種形式,因此建議盡量採用第二種方式。
// 關鍵字 委託類型 時間名 public event EventHandler PrintComplete;
EventHandler是在BCL中預定義的委託類型,它位於System命名空間,用以處理不包含事件數目據的事件。事件如果需要包含事件數目據,可以通過派生EventArgs實現。
先來看看EventHandler委託的簽名:
public delegate void EventHandler(Object sender,EventArgs e);
1、委託的傳回型別為void;
2、第一個參數--sender參數,它負責儲存觸發事件的對象的引用,因為參數的類型是Object類型,因此它可以儲存任何類型的執行個體;
3、第二個參數--e參數,它負責儲存事件數目據,這裡是在BCL中定義的預設的EventArgs類,它位於System命名空間中,他不能儲存任何資料。
四、訂閱事件
事件訂閱者角色需要訂閱事件發行者發布的事件,這樣才能在事件發布時接受到訊息並作出響應,事件事實上是委託類型,因此事件處理方法必須和委託簽名相匹配。如果事件使用預定義的委託類型:EventHandler,那麼匹配它的事件處理方法如下:
public void SomeEventHandler(object sender, EventArgs e) { //.. }
有了事件處理方法,就可以訂閱事件了,只需要使用加法賦值運算子(+=)即可。
五、觸發事件
//檢查事件是否為空白 if (PrintComplete != null) { //像調用方法一樣觸發事件,參數 PrintComplete(this,new EventArgs(); }
完整的一個事件例子:
namespace ConsoleApplication1{ public class Program { static void Main(string[] args) { Console.WriteLine("該做的東西做完,然後觸發事件!"); EventSample es = new EventSample(); es.ShowComplete += es.MyEventHandler; es.OnShowComplete(); Console.ReadKey(); } } public class EventSample {
//定義一個事件 public event EventHandler ShowComplete;
//觸發事件 public void OnShowComplete() { //判斷是否綁定了事件處理方法,null表示沒有事件處理方法 if (ShowComplete != null) {
//像調用方法一樣觸發事件 ShowComplete(this, new EventArgs()); } } //事件處理方法 public void MyEventHandler(object sender, EventArgs e) { Console.WriteLine("誰觸發了我?" + sender.ToString()); } }}
六、使用和擴充EventArgs類
前面提到過,預設的預定義委託EventHandler的第二個參數,本身不能包含事件數目據。但是在很多.net提供的方法,都能用e調用出某些資訊這是因為這不是預設的EventArgs類了。因此,在事件引發時不能向事件處理常式傳遞狀態資訊,如果要想傳遞狀態資訊,則需要從此類派生出一個類來儲存資訊。
以下為擴充EventArgs類的樣本:
public class PrintEventArgs : EventArgs { public string PrintState { get; set; } public PrintEventArgs(string state) { PrintState = state; } }
而在調用時,只需將EventArgs換成PrintEventArgs
public void SomeEventHandler(object sender, PrintEventArgs e) { Console.WriteLine("列印已完成!"); }
但是要注意此時,綁定事件編譯器會報錯:
enentSample.PrintComplete += ShowMessage; //此行代碼編譯器報錯
為什麼呢?因此事件委託的第二個參數是EventArgs類型與擴充的PrintEventArgs不同,因此不能在綁定舊的方法;所以就要使用自訂委託了。
七、自訂委託
既然EventHandler委託不能使用了,那麼就只有考慮使用自訂委託來聲明事件了。首先聲明一個自訂委託:
public delegate void PrintEventDelegate(object sender,PrintEventArgs e);
接下來,將事件聲明中使用的預定義委託EventDelegate換成我們自訂的委託:
public event PrintEventDelegate PrintComplete;
因為我們擴充的PrintEventArgs類沒有不帶參數的建構函式,因此需要修改事件觸發部分的代碼,傳遞一個參數進去,該參數的值就是要發送給事件處理方法的狀態資訊,這裡以一個簡單的字串代替:
if(PrintComplete != null) { PrintComplete(this,new PrintEventArgs("測試訊息")); }
八、事件訪問器
事件是特殊的多路廣播委託,事件預設有一個私人的委託類型變數,用以儲存對訂閱事件的事件處理方法的引用,此委託類型的變數僅能從聲明該事件的類中委託。事件訂閱者通過提供對事件處理方法的引用來訂閱事件,這些方法通過預設的時間訪問器添加到委託的調用列表中。這裡的事件訪問器類似於屬性訪問器,不同之處在於,時間訪問器被命名為add和remove,而不是屬性的get和set。在大多數情況下都不需要提供自訂的事件訪問器。如果沒有提供,則編譯器會自動添加事件訪問器。如果需要添加自訂事件訪問器,以支援某些自訂行為,可以使用如下文法:
public event MyEventHandler PrintComplete { add { //.. } remove { //.. } }
在聲明了事件訪問器以後,編譯器將不會提供私人的委派物件,此時對於訂閱者事件處理方法引用的管理需要我們自己去實現。
public event MyEventHandler PrintComplete { add { myEventHandler += value; } remove { myEventHandler -= value; } }
下面給出一個擴充EventArgs與自訂委託,傳遞資料到方法的樣本:
namespace ConsoleApplication1{ public class Program { static void Main(string[] args) { Console.WriteLine("該做的東西做完,然後觸發事件!"); EventSample es = new EventSample(); es.ShowComplete += es.MyEventHandler; es.OnShowComplete(); Console.ReadKey(); } } public class EventSample { //此事件已不能如此使用 //public event EventHandler ShowComplete; //自訂委託 public delegate void ShowEventDelegate(object sender,ShowEventArgs e); //將事件中的委託換成自己的自訂委託 public event ShowEventDelegate ShowComplete; public void OnShowComplete() { //判斷是否綁定了事件處理方法,null表示沒有事件處理方法 if (ShowComplete != null) { //這次要傳遞參數資料了 ShowComplete(this, new ShowEventArgs("傳給你的資料,接著吧!")); } } //事件處理方法,注意第二個參數 public void MyEventHandler(object sender, ShowEventArgs e) { Console.WriteLine("誰觸發了我 " + sender.ToString()); Console.WriteLine("傳過來什麼資料: " + e.ShowResult); } } //自訂EventArgs public class ShowEventArgs : EventArgs { public string ShowResult { get; set; } public ShowEventArgs(string result) { ShowResult = result; } }}
輸出結果如下: