事件是對(duì)象發(fā)送的通知,用于表示操作的發(fā)生。.NET中的事件遵循觀察者設(shè)計(jì)模式。
引發(fā)事件的類稱為 Publisher(發(fā)布者),接收通知的類稱為 Subscriber(訂閱者)。一個(gè)事件可以有多個(gè)訂閱者。通常,發(fā)布者在發(fā)生某個(gè)操作時(shí)引發(fā)事件。訂閱者希望在操作發(fā)生時(shí)獲得通知,他們應(yīng)該向事件注冊(cè)并處理它。
在C#中,事件是封裝的委托。它取決于委托。委托為訂閱者類的事件處理程序方法定義簽名。
下圖說(shuō)明了C#中的事件。
事件在類中聲明且生成,且通過(guò)使用同一個(gè)類或其他類中的委托與事件處理程序關(guān)聯(lián)。包含事件的類用于發(fā)布事件。這被稱為 發(fā)布者(publisher) 類。其他接受該事件的類被稱為 訂閱者(subscriber) 類。事件使用 發(fā)布-訂閱(publisher-subscriber) 模型。
發(fā)布者(publisher)- 是一個(gè)包含事件和委托定義的對(duì)象。事件和委托之間的聯(lián)系也定義在這個(gè)對(duì)象中。發(fā)布者(publisher)類的對(duì)象調(diào)用這個(gè)事件,并通知其他的對(duì)象。
訂閱者(subscriber)- 是一個(gè)接受事件并提供事件處理程序的對(duì)象。在發(fā)布者(publisher)類中的委托調(diào)用訂閱者(subscriber)類中的方法(事件處理程序)。
可以通過(guò)兩個(gè)步驟聲明一個(gè)事件:
聲明委托
使用 event 關(guān)鍵字聲明委托的變量
下面的示例演示如何在發(fā)布者類中聲明事件。
public delegate void Notify(); // 委托 public class ProcessBusinessLogic { public event Notify ProcessCompleted; // 事件 }
在上面的示例中,我們聲明了一個(gè)委托 Notify,然后在 ProcessBusinessLogic 類中使用“event”關(guān)鍵字聲明了委托類型Notify的事件ProcessCompleted。因此,ProcessBusinessLogic類稱為publisher(發(fā)布者)。Notify委托指定ProcessCompleted事件處理程序的簽名。它指定subscriber(訂閱者)類中的事件處理程序方法必須具有 void 返回類型,并且沒(méi)有參數(shù)。
現(xiàn)在,讓我們看看如何引發(fā)ProcessCompleted事件。請(qǐng)看以下實(shí)現(xiàn)。
public delegate void Notify(); // 委托 public class ProcessBusinessLogic { public event Notify ProcessCompleted; // 事件 public void StartProcess() { Console.WriteLine("Process Started!"); // 一些代碼在這里.. OnProcessCompleted(); } protected virtual void OnProcessCompleted() //受保護(hù)的虛方法 { //如果ProcessCompleted不為null,則調(diào)用委托 ProcessCompleted?.Invoke(); } }
上面,StartProcess()方法在末尾調(diào)用onProcessCompleted()方法,這將引發(fā)一個(gè)事件。通常,要引發(fā)事件,應(yīng)使用<EventName>上的名稱定義protected和virtual方法。受保護(hù)和虛擬使派生類重寫(xiě)引發(fā)事件的邏輯。但是,派生類應(yīng)該始終調(diào)用基類的On<EventName>方法,以確保注冊(cè)的委托接收事件。
OnProcessCompleted() 方法使用 ProcessCompleted?. invoke() 調(diào)用委托。這將調(diào)用所有注冊(cè)到 ProcessCompleted 事件的事件處理程序方法。
訂閱者類必須注冊(cè)到ProcessCompleted事件,并使用簽名匹配Notify委托的方法來(lái)處理它,如下所示。
class Program { public static void Main() { ProcessBusinessLogic bl = new ProcessBusinessLogic(); bl.ProcessCompleted += bl_ProcessCompleted; // 注冊(cè)事件 bl.StartProcess(); } // 事件處理程序 public static void bl_ProcessCompleted() { Console.WriteLine("Process Completed!"); } }
上面,Program 類是 ProcessCompleted 事件的訂閱者。它使用 + = 運(yùn)算符向事件注冊(cè)。請(qǐng)記住,這與我們?cè)诙嗖ノ械恼{(diào)用列表中添加方法的方式是一樣的。bl_processcompleted ()方法處理該事件,因?yàn)樗c Notify 委托的簽名匹配。
.NET Framework包含用于最常見(jiàn)事件的內(nèi)置委托類型EventHandler和EventHandler <TEventArgs>。通常,任何事件都應(yīng)包括兩個(gè)參數(shù):事件源和事件數(shù)據(jù)。。對(duì)不包含事件數(shù)據(jù)的所有事件使用EventHandler委托。對(duì)于包含要發(fā)送到處理程序的數(shù)據(jù)的事件,請(qǐng)使用 EventHandler<TEventArgs> 委托。
上面顯示的示例可以使用EventHandler委托,而無(wú)需聲明自定義Notify委托,如下所示。
class Program { public static void Main() { ProcessBusinessLogic bl = new ProcessBusinessLogic(); bl.ProcessCompleted += bl_ProcessCompleted; // 事件注冊(cè) bl.StartProcess(); } // 事件處理 public static void bl_ProcessCompleted(object sender, EventArgs e) { Console.WriteLine("Process Completed!"); } } public class ProcessBusinessLogic { // 使用內(nèi)置EventHandler聲明事件 public event EventHandler ProcessCompleted; public void StartProcess() { Console.WriteLine("Process Started!"); // 一些代碼在這里.. OnProcessCompleted(EventArgs.Empty); //無(wú)事件數(shù)據(jù) } protected virtual void OnProcessCompleted(EventArgs e) { ProcessCompleted?.Invoke(this, e); } }
在上面的示例中,事件處理程序bl_ProcessCompleted()方法包含兩個(gè)與 EventHandler 委托匹配的參數(shù)。同時(shí),傳遞 this 作為發(fā)送者和EventArgs。當(dāng)我們?cè)贠nProcessCompleted()方法中使用Invoke()引發(fā)事件時(shí)為空。因?yàn)槲覀兊氖录恍枰魏螖?shù)據(jù),它只是通知訂閱者流程已經(jīng)完成,所以我們傳遞了 EventArgs.Empty。
大多數(shù)事件向訂閱者發(fā)送一些數(shù)據(jù)。EventArgs類是所有事件數(shù)據(jù)類的基類。NET包含許多內(nèi)置事件數(shù)據(jù)類,如 SerialDataReceivedEventArgs。它遵循以EventArgs結(jié)束所有事件數(shù)據(jù)類的命名模式。您可以通過(guò)派生 EventArgs 類為事件數(shù)據(jù)創(chuàng)建自定義類。
使用EventHandler <TEventArgs>將數(shù)據(jù)傳遞到處理程序,如下所示。
class Program { public static void Main() { ProcessBusinessLogic bl = new ProcessBusinessLogic(); bl.ProcessCompleted += bl_ProcessCompleted; // 事件注冊(cè) bl.StartProcess(); } // 事件處理 public static void bl_ProcessCompleted(object sender, bool IsSuccessful) { Console.WriteLine("Process " + (IsSuccessful? "Completed Successfully": "failed")); } } public class ProcessBusinessLogic { // 使用內(nèi)置EventHandler聲明事件 public event EventHandler<bool> ProcessCompleted; public void StartProcess() { try { Console.WriteLine("Process Started!"); // 一些代碼在這里.. OnProcessCompleted(true); } catch(Exception ex) { OnProcessCompleted(false); } } protected virtual void OnProcessCompleted(bool IsSuccessful) { ProcessCompleted?.Invoke(this, IsSuccessful); } }
在上面的示例中,我們將單個(gè)布爾值傳遞給處理程序,以指示該過(guò)程是否成功完成。
如果希望將多個(gè)值作為事件數(shù)據(jù)傳遞,那么可以創(chuàng)建一個(gè)從 EventArgs 基類派生的類,如下所示。
class ProcessEventArgs : EventArgs { public bool IsSuccessful { get; set; } public DateTime CompletionTime { get; set; } }
下面的示例演示如何將自定義 ProcessEventArgs 類傳遞給處理程序。
class Program { public static void Main() { ProcessBusinessLogic bl = new ProcessBusinessLogic(); bl.ProcessCompleted += bl_ProcessCompleted; // 事件注冊(cè) bl.StartProcess(); } // 事件處理 public static void bl_ProcessCompleted(object sender, ProcessEventArgs e) { Console.WriteLine("Process " + (e.IsSuccessful? "Completed Successfully": "failed")); Console.WriteLine("Completion Time: " + e.CompletionTime.ToLongDateString()); } } public class ProcessBusinessLogic { // 使用內(nèi)置EventHandler聲明事件 public event EventHandler<ProcessEventArgs> ProcessCompleted; public void StartProcess() { var data = new ProcessEventArgs(); try { Console.WriteLine("Process Started!"); // 一些代碼在這里.. data.IsSuccessful = true; data.CompletionTime = DateTime.Now; OnProcessCompleted(data); } catch(Exception ex) { data.IsSuccessful = false; data.CompletionTime = DateTime.Now; OnProcessCompleted(data); } } protected virtual void OnProcessCompleted(ProcessEventArgs e) { ProcessCompleted?.Invoke(this, e); } }
因此,您可以在C#中創(chuàng)建,引發(fā),注冊(cè)和處理事件。
事件是對(duì)委托的包裝。這取決于委托。
將“ event”關(guān)鍵字與委托類型變量一起使用來(lái)聲明事件。
將內(nèi)置委托EventHandler或EventHandler <TEventArgs>用于常見(jiàn)事件。
發(fā)布者類引發(fā)一個(gè)事件,而訂閱者類注冊(cè)一個(gè)事件并提供事件處理程序方法。
用事件名稱命名引發(fā)事件的方法,該方法以“ On”為前綴。
處理程序方法的簽名必須與委托簽名匹配。
使用+ =運(yùn)算符注冊(cè)事件。使用 -= 運(yùn)算符取消訂閱,不能使用=運(yùn)算符。
使用EventHandler <TEventArgs>傳遞事件數(shù)據(jù)。
派生EventArgs基類以創(chuàng)建自定義事件數(shù)據(jù)類。
可以將事件聲明為靜態(tài),虛擬,密封和抽象(static, virtual, sealed, abstract)。
接口可以將事件作為成員包含。
如果有多個(gè)訂閱者,則將同步調(diào)用事件處理程序。