1、
.NET 中的事件基于委托模型。 委托模型遵循觀察者設計模式,使訂閱者能夠向提供方注冊并接收相關通知。 事件發(fā)送方推送事件發(fā)生的通知,事件接收器接收該通知并定義對它的響應。 本文介紹委托模型的主要組件、如何在應用程序中使用事件以及如何在你的代碼中實現(xiàn)事件。
有關在 Windows 8.x Store 應用中處理事件的信息,請參閱事件和路由事件概述。
事件是由對象發(fā)送的用于發(fā)出操作信號的消息。 該操作可能是由用戶交互引起,例如單擊按鈕;也可能是由某些其他程序的邏輯導致,例如更改屬性值。 引發(fā)事件的對象稱為“事件發(fā)送方”。 事件發(fā)送方不知道哪個對象或方法將接收(處理)它引發(fā)的事件。 事件通常是事件發(fā)送方的成員,例如 Click 事件是 Button 類的成員,PropertyChanged 事件是實現(xiàn) INotifyPropertyChanged 接口的類的成員。
若要定義一個事件,可以在事件類簽名中使用 event
(在 C# 中)或 Event
(在 Visual Basic 中)關鍵字,并指定事件的委托類型。 委托在下一節(jié)中介紹。
通常,為了引發(fā)事件,您可以在 C# 中添加一個標記為 protected
和 virtual
或在 Visual Basic 中標記為 Protected
和 Overridable
的方法。 將此方法命名為 On
EventName;例如,OnDataReceived
。 此方法應接受一個指定事件數(shù)據(jù)對象(EventArgs 類型或派生類型)的參數(shù)。 您提供此方法以允許派生類重寫引發(fā)事件的邏輯。 派生類應始終調用基類的 On
EventName 方法,以確保注冊的委托接收事件。
下面的示例顯示如何聲明名為 ThresholdReached
事件。 該事件與 EventHandler 委托相關聯(lián)并且由 OnThresholdReached
方法引發(fā)。
C#復制
- class Counter
- {
- public event EventHandler ThresholdReached;
- protected virtual void OnThresholdReached(EventArgs e)
- {
- EventHandler handler = ThresholdReached;
- handler?.Invoke(this, e);
- }
- // provide remaining implementation for the class
- }
委托是一種保存對方法的引用的類型。 委托是通過顯示所引用方法的返回類型和參數(shù)的簽名來聲明的,并可以僅保存與其簽名匹配的方法的引用。 因此,委托等同于類型安全函數(shù)指針或回叫。 委托聲明足以定義委托類。
委托在 .NET 中有許多用途。 在事件上下文中,委托是事件源和處理事件的代碼之間的媒介(或類似指針的機制)。 如上一節(jié)的例子所示,可以通過在事件聲明中包括委托類型來將委托和事件相關聯(lián)。 有關委托的詳細信息,請參閱 Delegate 類。
.NET 提供了 EventHandler 和 EventHandler<TEventArgs> 委托來支持大部分事件場景。 使用 EventHandler 委托處理不包含事件數(shù)據(jù)的所有事件。 使用 EventHandler<TEventArgs> 委托處理包含事件相關數(shù)據(jù)的事件。 這些委托沒有返回類型值,并且接受兩個參數(shù)(事件源的對象和事件數(shù)據(jù)的對象)。
委托是多播,這意味著它們可以保存對多個事件處理方法的引用。 有關詳細信息,請參閱 Delegate 參考頁。 委托提供了事件處理中的靈活性和精確控制。 委托人通過維護事件的已注冊事件處理程序列表來充當引發(fā)事件的類的事件調度程序。
在 EventHandler 和 EventHandler<TEventArgs> 委托不可用的場景下,您可以定義一個委托。 要求你定義委托的場景非常少見的,例如,當你必須處理無法識別泛型的代碼時。 在聲明中使用 delegate
(在 C# 中)和 Delegate
(在 Visual Basic 中)關鍵字標記委托。 下面的示例說明如何聲明 ThresholdReachedEventHandler
委托。
C#復制
public delegate void ThresholdReachedEventHandler(object sender, ThresholdReachedEventArgs e);
與事件相關的數(shù)據(jù)可以通過事件數(shù)據(jù)類提供。 .NET 提供了許多事件數(shù)據(jù)類,用戶可以在自己的應用程序中使用它們。 例如,SerialDataReceivedEventArgs 類是 SerialPort.DataReceived 事件的事件數(shù)據(jù)類。 .NET 遵循所有事件數(shù)據(jù)類以 EventArgs
結尾的命名模式。 您通過查看事件的委托來確定哪個事件數(shù)據(jù)類與事件相關聯(lián)。 例如,SerialDataReceivedEventHandler 委托包含 SerialDataReceivedEventArgs 類作為它的一個參數(shù)。
EventArgs 類是所有事件數(shù)據(jù)類的基類型。 當一個事件沒有任何與其相關聯(lián)的數(shù)據(jù)時,您也會用到 EventArgs 類。 當您創(chuàng)建一個事件僅用來通知其他類出問題了,不需要傳遞任何數(shù)據(jù)時,請包括 EventArgs 類作為委托中的第二個參數(shù)。 當沒有數(shù)據(jù)提供時,您可以傳遞 EventArgs.Empty 值。 EventHandler 委托包括 EventArgs 類作為一個參數(shù)。
當您想創(chuàng)建一個自定義的事件數(shù)據(jù)類時,請創(chuàng)建一個派生自 EventArgs 的類,然后提供所需的所有成員,來傳遞與該事件相關的數(shù)據(jù)。 通常,應使用與 .NET 相同的命名模式,并且事件數(shù)據(jù)類名稱應以 EventArgs
結尾。
下面的示例演示了一個名為 ThresholdReachedEventArgs
的事件數(shù)據(jù)類。 它包含特定于引發(fā)事件的屬性。
C#復制
- public class ThresholdReachedEventArgs : EventArgs
- {
- public int Threshold { get; set; }
- public DateTime TimeReached { get; set; }
- }
若要響應事件,您需要在事件接收器中定義一個事件處理程序方法。 此方法必須與您正處理的事件的委托簽名匹配。 在事件處理程序中,當事件引發(fā)時會執(zhí)行所需操作,例如在用戶單擊按鈕之后收集用戶輸入。 若當事件發(fā)生時收到通知,您的事件處理程序方法必須訂閱該事件。
下面的示例演示與 c_ThresholdReached
委托的簽名匹配的 EventHandler 事件處理程序方法。 該方法訂閱 ThresholdReached
事件。
C#復制
- class Program
- {
- static void Main()
- {
- var c = new Counter();
- c.ThresholdReached += c_ThresholdReached;
- // provide remaining implementation for the class
- }
- static void c_ThresholdReached(object sender, EventArgs e)
- {
- Console.WriteLine("The threshold was reached.");
- }
- }
借助 .NET,訂閱者可以進行靜態(tài)或動態(tài)注冊以獲得事件通知。 對于其事件由靜態(tài)事件處理程序進行處理的類,靜態(tài)事件處理程序對其整個生命周期有效。 通常為響應某些條件程序邏輯,會在程序執(zhí)行期間顯式激活和停用動態(tài)事件處理程序。 例如,如果僅在特定條件下需要事件通知,或如果應用程序提供多個事件處理程序且由運行時條件定義要使用的適當事件處理程序,則可以使用動態(tài)事件處理程序。 上一節(jié)中的示例演示如何動態(tài)添加事件處理程序。 有關詳細信息,請查看 Visual Basic 中的事件和 C# 中的事件。
如果您的類引發(fā)多個事件,編譯器會為每一個事件委托實例生成一個字段。 如果事件數(shù)量很大,則可能無法接受按一個委托計算一個字段的存儲成本。 對于這些情況,.NET 提供一個事件屬性,可以將其與選擇的另一數(shù)據(jù)結構一起用于存儲事件委托。
事件屬性由事件聲明和事件訪問器組成。 事件訪問器是您定義的方法,用來從存儲數(shù)據(jù)結構添加和移除事件委托實例。 請注意,事件屬性要比事件字段慢,這是因為必須先檢索每個事件委托,然后才能調用它。 需在內存和速度之間進行權衡。 如果類定義許多不常引發(fā)的事件,那么需要實現(xiàn)事件屬性。 有關詳細信息,請參閱如何:使用事件屬性處理多個事件。
Title | 描述 |
---|---|
如何:拋出和使用事件 | 包含引發(fā)和使用事件的示例。 |
如何:使用事件屬性處理多個事件 | 演示如何使用事件屬性處理多個事件。 |
觀察程序設計模式 | 描述允許訂閱者向提供方注冊和接收通知的設計模式。 |
如何:在 Web 窗體應用程序中使用事件 | 演示如何處理 Web 窗體控件引發(fā)的事件。 |