.NET 中的事件是以委派模型為基礎。 委派模型遵循 觀察者設計模式,可讓訂閱者向提供者註冊並接收通知。 事件發生時,事件傳送者會推播通知。 事件接收器會定義回應。 本文說明委派模型的主要元件、如何在應用程式中取用事件,以及如何在程式代碼中實作事件。
以事件發送器觸發事件
事件是由 物件傳送的訊息,以發出動作的出現訊號。 動作可能是用戶互動,例如按下按鈕,或可能是其他程式邏輯所造成的,例如屬性值變更。 引發事件的 對象稱為 事件傳送者。 事件傳送者不知道接收事件的物件或處理方法。 事件通常是事件發送者的一個成員。 例如, Click 事件是 類別的成員 Button ,而 PropertyChanged 事件是實作 介面的類別 INotifyPropertyChanged 成員。
若要定義事件,您可以在事件類別的簽章中使用 C# 事件 或 Visual Basic Event 關鍵詞,並指定事件的委派類型。 下一節會介紹代表。
一般而言,若要引發事件,您可以新增標示為 protected
和 virtual
的方法(在 C# 中),或標示為 Protected
和 Overridable
的方法(在 Visual Basic 中)。 方法的命名慣例為 On<EventName>
,例如 OnDataReceived
。 方法應該接受一個參數來指定事件數據物件,該物件是型別 EventArgs 或其衍生型別的物件。 您可以提供這個方法,讓衍生類別覆寫引發事件的邏輯。 衍生類別應該一律呼叫 On<EventName>
基類的 方法,以確保已註冊的委派會收到事件。
下列範例示範如何宣告名為 ThresholdReached
的事件。 事件與委派 EventHandler 相關聯,並在名為 OnThresholdReached
的方法中引發。
class Counter
{
public event EventHandler ThresholdReached;
protected virtual void OnThresholdReached(EventArgs e)
{
ThresholdReached?.Invoke(this, e);
}
// provide remaining implementation for the class
}
Public Class Counter
Public Event ThresholdReached As EventHandler
Protected Overridable Sub OnThresholdReached(e As EventArgs)
RaiseEvent ThresholdReached(Me, e)
End Sub
' provide remaining implementation for the class
End Class
宣告事件處理程式的委派簽章
委派是一種持有方法引用的類型。 委派會以簽名宣告,該簽名顯示其所參考方法的傳回類型和參數。 它只能保存符合其簽章之方法的參考。 委派等同於類型安全的函式指標或回呼。 委派宣告足以定義委派類別。
委派在 .NET 中有許多用途。 在事件內容中,委派是事件來源與處理事件之程式代碼之間的媒介(或類似指標的機制)。 您可以將委派與事件產生關聯,方法是在事件宣告中包含委派類型,如上一節中的範例所示。 如需委派的詳細資訊,請參閱 類別 Delegate 。
.NET 提供 EventHandler 和 EventHandler<TEventArgs> 委派來支援大部分的事件情境。 針對所有不包含事件資料的事件使用EventHandler委派。 對於包含事件資料的事件,請使用EventHandler<TEventArgs> 委派。 這些委派沒有傳回型別值,並採用兩個參數(事件來源的物件,以及事件數據的物件)。
委派是 多播 類別物件,這表示它們可以保存多個事件處理方法的參考。 如需詳細資訊,請參閱 Delegate 參考頁面。 委派可在事件處理中提供彈性和精細的控制。 委派做為引發事件的類別的事件調度器,負責維護事件之已註冊事件處理程式的清單。
使用 EventHandler 和 EventHandler<TEventArgs> 委派類型來定義所需的委派。 您可以在宣告中將委派標記為 [C#](../../csharp/language-reference/builtin-types/reference-types.md#the-delegate-type) 的 delegate
型別,或在 Visual Basic 中標記為 Delegate
型別。 下列範例示範如何宣告一個名為 ThresholdReachedEventHandler
的委派:
public delegate void ThresholdReachedEventHandler(object sender, ThresholdReachedEventArgs e);
Public Delegate Sub ThresholdReachedEventHandler(sender As Object, e As ThresholdReachedEventArgs)
使用事件數據類別
您可以透過事件資料類別來提供與事件相關聯的數據。 .NET 提供許多可在應用程式中使用的事件數據類別。 例如,類別 SerialDataReceivedEventArgs 是事件的事件數據類別 SerialPort.DataReceived 。 .NET 遵循命名模式,其中所有事件數據類別的結尾 EventArgs
都是後綴。 您可以藉由查看事件的委派,判斷哪個事件數據類別與事件相關聯。 例如, SerialDataReceivedEventHandler 委派包含 SerialDataReceivedEventArgs 類別做為參數。
類別 EventArgs 通常是事件數據類別的基底類型。 如果事件沒有任何相關聯的數據,您也可以使用此類別。 當您建立一個不包含任何額外數據的事件以通知訂閱者某事已發生時,請將 EventArgs 類別作為第二個參數包含在委派中。 當未提供任何數據時,您可以傳遞 EventArgs.Empty 值。 委派 EventHandler 包含 EventArgs 類別做為參數。
您可以建立衍生自 EventArgs 類別的類別,以提供任何需要的成員來傳遞與事件相關的資料。 一般而言,您應該使用與 .NET 相同的命名模式,並以 EventArgs
作為事件數據類別名稱的後綴結尾。
下列範例顯示名為 ThresholdReachedEventArgs
的事件數據類別,其中包含所引發事件特有的屬性:
public class ThresholdReachedEventArgs : EventArgs
{
public int Threshold { get; set; }
public DateTime TimeReached { get; set; }
}
Public Class ThresholdReachedEventArgs
Inherits EventArgs
Public Property Threshold As Integer
Public Property TimeReached As DateTime
End Class
使用處理程序回應事件
若要回應事件,您可以在事件接收器中定義事件處理程式方法。 這個方法必須符合您要處理之事件的委派簽章。 在事件處理程式中,您會執行引發事件時所需的動作,例如在使用者按下按鈕之後收集用戶輸入。 若要在事件發生時接收通知,事件處理程式方法必須訂閱事件。
下列範例顯示名為 c_ThresholdReached
的事件處理程式方法,其符合委派的 EventHandler 簽章。 方法會訂閱ThresholdReached
事件:
class ProgramTwo
{
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.");
}
}
Module Module1
Sub Main()
Dim c As New Counter()
AddHandler c.ThresholdReached, AddressOf c_ThresholdReached
' provide remaining implementation for the class
End Sub
Sub c_ThresholdReached(sender As Object, e As EventArgs)
Console.WriteLine("The threshold was reached.")
End Sub
End Module
使用靜態和動態事件處理程式
.NET 可讓訂閱者以靜態或動態方式註冊事件通知。 在其事件處理的類別的整個生命週期中,靜態事件處理程式都會生效。 動態事件處理程式會在程式執行期間明確啟動和停用,通常是為了回應某些條件式程序邏輯。 只有在特定條件下才需要事件通知,或運行時間條件決定要呼叫的特定處理程式時,您可以使用動態處理程式。 上一節中的範例示範如何動態新增事件處理程式。 如需詳細資訊,請參閱 事件 (在 Visual Basic 中) 和 事件 (在 C# 中)。
引發多個事件
如果您的類別引發多個事件,編譯程式就會為每個事件委派實例產生一個字段。 如果事件數量很大,那麼每個代理的單個字段的儲存成本可能會過高而無法接受。 在這些案例中,.NET 提供事件屬性,您可以搭配您選擇的另一個數據結構來儲存事件委派。
事件屬性由事件宣告及其伴隨的事件存取子組成。 事件存取子是用來從記憶體數據結構新增或移除事件委派實例的方法。
備註
事件屬性運行速度比事件欄位慢,因為每個事件委派必須先被取得才能被叫用。
取捨在於記憶體和速度之間。 如果您的類別定義許多不常引發的事件,您應該實作事件屬性。 如需詳細資訊,請參閱 使用事件屬性處理多個事件。
探索相關任務
使用以下資源來了解與事件相關的其他工作和概念:
- 引發和取用事件:尋找引發和取用事件的範例。
- 使用事件屬性處理多個事件:探索如何使用事件屬性來處理多個事件。
- 探索觀察者設計模式:檢閱可讓訂閱者向提供者註冊及接收通知的設計模式。
查看規格的參考
規格參考檔案適用於支援事件處理的 API:
API 名稱 | API 類型 | 參考文獻 |
---|---|---|
EventHandler | 代表 | EventHandler |
EventHandler<TEventArgs> | 代表 | EventHandler<TEventArgs> |
EventArgs | 班級 | EventArgs |
代表 | 班級 | Delegate |