處理和引發事件Handling and raising events

.NET 中的事件是以委派模型為基礎。Events in .NET are based on the delegate model. 委派模型遵循觀察者設計模式,它可讓訂閱者向提供者註冊,並且接收通知。The delegate model follows the observer design pattern, which enables a subscriber to register with and receive notifications from a provider. 事件發送者會推播事件已發生的通知,而事件接收器會收到該通告並定義對它的回應。An event sender pushes a notification that an event has happened, and an event receiver receives that notification and defines a response to it. 本文將描述委派模型的主要元件、如何在應用程式中使用事件,以及如何在程式碼中實作事件。This article describes the major components of the delegate model, how to consume events in applications, and how to implement events in your code.

如需處理 Windows 8.x Microsoft Store 應用程式中事件的詳細資訊,請參閱事件和路由事件概觀For information about handling events in Windows 8.x Store apps, see Events and routed events overview.

「事件」Events

事件是由物件傳送的訊息,用以表示發生動作。An event is a message sent by an object to signal the occurrence of an action. 這個動作可能是因使用者互動所造成,例如按一下按鈕,也可能起因於其他程式邏輯,如變更屬性值。The action can be caused by user interaction, such as a button click, or it can result from some other program logic, such as changing a property’s value. 引發事件的物件稱為「事件發送者」。The object that raises the event is called the event sender. 事件發送者並不清楚哪個物件或方法會接收 (處理) 它所引發的事件。The event sender doesn't know which object or method will receive (handle) the events it raises. 事件通常是事件發送者的成員,例如,Click 事件是 Button 類別的成員,而 PropertyChanged 事件是實作 INotifyPropertyChanged 介面之類別的成員。The event is typically a member of the event sender; for example, the Click event is a member of the Button class, and the PropertyChanged event is a member of the class that implements the INotifyPropertyChanged interface.

若要定義事件,您可以在事件類別的特徵標記中使用 C# 的 event 或 Visual Basic 的 Event 關鍵字,並指定事件之委派的型別。To define an event, you use the C# event or the Visual Basic Event keyword in the signature of your event class, and specify the type of delegate for the event. 委派將在下一節中描述。Delegates are described in the next section.

一般而言,若要引發事件,您會加入標記為 protectedvirtual (C#) 或 ProtectedOverridable (Visual Basic) 的方法。Typically, to raise an event, you add a method that is marked as protected and virtual (in C#) or Protected and Overridable (in Visual Basic). 為這個 OnEventName 方法命名,例如 OnDataReceivedName this method OnEventName; for example, OnDataReceived. 這個方法應該接受一個指定事件資料物件 (EventArgs 型別或衍生型別之物件) 的參數。The method should take one parameter that specifies an event data object, which is an object of type EventArgs or a derived type. 您可以提供這個方法讓衍生類別覆寫引發事件的邏輯。You provide this method to enable derived classes to override the logic for raising the event. 衍生類別一定要呼叫基底類別的 OnEventName 方法,以確保註冊的委派收到事件。A derived class should always call the OnEventName method of the base class to ensure that registered delegates receive the event.

下列範例將示範如何宣告名為 ThresholdReached 的事件。The following example shows how to declare an event named ThresholdReached. 事件與 EventHandler 委派有關,而且會在名為 OnThresholdReached 的方法中引發。The event is associated with the EventHandler delegate and raised in a method named OnThresholdReached.

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
}
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

委派Delegates

委派是保存方法之參考的類型。A delegate is a type that holds a reference to a method. 委派是透過特徵標記來宣告,該特徵標記會顯示所參考方法的傳回型別和參數,而且委派只能持有符合其特徵標記之方法的參考。A delegate is declared with a signature that shows the return type and parameters for the methods it references, and it can hold references only to methods that match its signature. 委派因此相當於 type-safe 函式指標或回呼。A delegate is thus equivalent to a type-safe function pointer or a callback. 委派宣告不足以定義委派類別。A delegate declaration is sufficient to define a delegate class.

委派在 .NET 中有多種用途。Delegates have many uses in .NET. 在事件中,委派是事件來源和處理事件的程式碼之間的媒介 (或類似指標的機制)。In the context of events, a delegate is an intermediary (or pointer-like mechanism) between the event source and the code that handles the event. 將委派類型加入事件宣告中就可以讓委派與事件產生關聯,如上一節的範例所示。You associate a delegate with an event by including the delegate type in the event declaration, as shown in the example in the previous section. 如需委派的詳細資訊,請參閱 Delegate 類別。For more information about delegates, see the Delegate class.

.NET 提供了 EventHandlerEventHandler<TEventArgs> 委派來支援大多數事件案例。.NET provides the EventHandler and EventHandler<TEventArgs> delegates to support most event scenarios. 請針對所有沒有事件資料的事件使用 EventHandler 委派。Use the EventHandler delegate for all events that do not include event data. 請針對有事件相關資料的事件使用 EventHandler<TEventArgs> 委派。Use the EventHandler<TEventArgs> delegate for events that include data about the event. 這些委派沒有傳回型別值,而且會接受兩個參數 (事件來源的物件和事件資料的物件)。These delegates have no return type value and take two parameters (an object for the source of the event, and an object for event data).

委派為多點傳送,這表示它們可以持有對一個以上事件處理方法的參考。Delegates are multicast, which means that they can hold references to more than one event-handling method. 如需詳細資訊,請參閱 Delegate 參考頁面。For details, see the Delegate reference page. 委派可讓事件處理更彈性並進行精細的控制。Delegates provide flexibility and fine-grained control in event handling. 委派會維護事件的已註冊事件處理常式清單,進而做為引發事件之類別的事件分派者。A delegate acts as an event dispatcher for the class that raises the event by maintaining a list of registered event handlers for the event.

對於無法使用 EventHandlerEventHandler<TEventArgs> 委派的情況,您可以定義委派。For scenarios where the EventHandler and EventHandler<TEventArgs> delegates do not work, you can define a delegate. 需要定義委派的情況非常少見,例如,當您必須使用無法辨識泛型的程式碼時。Scenarios that require you to define a delegate are very rare, such as when you must work with code that does not recognize generics. 您會在宣告中以 C# 的 delegate 與 Visual Basic 的 Delegate 關鍵字標記委派。You mark a delegate with the C# delegate and Visual Basic Delegate keyword in the declaration. 下列範例將示範如何宣告名為 ThresholdReachedEventHandler 的委派。The following example shows how to declare a delegate named ThresholdReachedEventHandler.

public delegate void ThresholdReachedEventHandler(object sender, ThresholdReachedEventArgs e);
Public Delegate Sub ThresholdReachedEventHandler(sender As Object, e As ThresholdReachedEventArgs)

事件資料Event data

與事件相關聯的資料可以透過事件資料類別提供。Data that is associated with an event can be provided through an event data class. .NET 提供了許多事件資料類別,可讓您在應用程式中使用。.NET provides many event data classes that you can use in your applications. 例如,SerialDataReceivedEventArgs 類別是 SerialPort.DataReceived 事件的事件資料類別。For example, the SerialDataReceivedEventArgs class is the event data class for the SerialPort.DataReceived event. .NET 遵循的命名模式是以 EventArgs 作為所有事件資料類型的結尾。.NET follows a naming pattern of ending all event data classes with EventArgs. 您可以藉由查看事件的委派,判斷與事件相關聯的事件資料類別。You determine which event data class is associated with an event by looking at the delegate for the event. 例如,SerialDataReceivedEventHandler 委派會包含 SerialDataReceivedEventArgs 類別做為其中一個參數。For example, the SerialDataReceivedEventHandler delegate includes the SerialDataReceivedEventArgs class as one of its parameters.

EventArgs 類別是所有事件資料類別的基底類型。The EventArgs class is the base type for all event data classes. EventArgs 也是您在事件沒有任何相關資料時使用的類別。EventArgs is also the class you use when an event does not have any data associated with it. 如果您建立的事件主要工作只是通知其他類別有事件發生,而不需要傳遞任何資料,請在委派中加入 EventArgs 類別做為第二個參數。When you create an event that is only meant to notify other classes that something happened and does not need to pass any data, include the EventArgs class as the second parameter in the delegate. 您可以在未提供資料時傳遞 EventArgs.Empty 值。You can pass the EventArgs.Empty value when no data is provided. EventHandler 委派會包含 EventArgs 類別做為參數。The EventHandler delegate includes the EventArgs class as a parameter.

當您要建立自訂事件資料類別時,請建立衍生自 EventArgs 的類別,然後提供傳遞事件相關資料所需的所有成員。When you want to create a customized event data class, create a class that derives from EventArgs, and then provide any members needed to pass data that is related to the event. 一般而言,您應該使用與 .NET 相同的命名模式,並以 EventArgs 作為事件資料類別名稱的結尾。Typically, you should use the same naming pattern as .NET and end your event data class name with EventArgs.

下列範例將示範名為 ThresholdReachedEventArgs 的事件資料類別。The following example shows an event data class named ThresholdReachedEventArgs. 其中包含所引發事件專屬的屬性。It contains properties that are specific to the event being raised.

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

事件處理常式Event handlers

若要回應事件,請在事件接收器中定義事件處理常式方法。To respond to an event, you define an event handler method in the event receiver. 這個方法必須符合您要處理之事件的委派簽章。This method must match the signature of the delegate for the event you are handling. 在事件處理常式中,您會在事件引發時執行必要的動作,例如,在使用者按一下按鈕後收集使用者輸入。In the event handler, you perform the actions that are required when the event is raised, such as collecting user input after the user clicks a button. 若要在事件發生時收到通告,則事件處理常式方法必須訂閱事件。To receive notifications when the event occurs, your event handler method must subscribe to the event.

下列範例將示範名為 c_ThresholdReached 的事件處理常式方法,該方法會比對 EventHandler 委派的簽章。The following example shows an event handler method named c_ThresholdReached that matches the signature for the EventHandler delegate. 方法會訂閱 ThresholdReached 事件。The method subscribes to the ThresholdReached event.

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.");
    }
}
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

靜態和動態事件處理常式Static and dynamic event handlers

.NET 可讓訂閱者以靜態或動態方式註冊事件通知。.NET allows subscribers to register for event notifications either statically or dynamically. 靜態事件處理常式在其處理事件之類別的整個生命週期內都有效。Static event handlers are in effect for the entire life of the class whose events they handle. 動態事件處理常式會在程式執行期間明確地啟動及停用,通常是為了回應某些條件式邏輯。Dynamic event handlers are explicitly activated and deactivated during program execution, usually in response to some conditional program logic. 比方說,如果只有在某些情況下才需要事件通知,或如果應用程式提供多個事件處理常式且執行階段條件會定義一個要使用的適當處理常式,即可使用動態事件處理常式。For example, they can be used if event notifications are needed only under certain conditions or if an application provides multiple event handlers and run-time conditions define the appropriate one to use. 上一節中的範例示範了如何以動態方式加入事件處理常式。The example in the previous section shows how to dynamically add an event handler. 如需詳細資訊,請參閱事件 (in Visual Basic) 及事件 (in C#)。For more information, see Events (in Visual Basic) and Events (in C#).

引發多個事件Raising multiple events

如果您的類別會引發多個事件,編譯器將會為每個事件委派執行個體產生一個欄位。If your class raises multiple events, the compiler generates one field per event delegate instance. 如果事件數目很大,則可能無法接受每個委派一個欄位的儲存成本。If the number of events is large, the storage cost of one field per delegate may not be acceptable. 對於這些情況,.NET 提供了事件屬性,可以搭配您選擇的另一種資料結構來儲存事件委派。For those situations, .NET provides event properties that you can use with another data structure of your choice to store event delegates.

事件屬性是由事件存取子伴隨的事件宣告所組成。Event properties consist of event declarations accompanied by event accessors. 事件存取子是您定義用來將事件委派執行個體加入儲存區資料結構或從其中移除的方法。Event accessors are methods that you define to add or remove event delegate instances from the storage data structure. 請注意,事件屬性會比事件欄位來得慢些,因為每個事件委派都必須先擷取,然後才能夠叫用。Note that event properties are slower than event fields, because each event delegate must be retrieved before it can be invoked. 記憶體和速度之間會有所折衷。The trade-off is between memory and speed. 如果您的類別定義許多不常引發的事件,您會想要實作事件屬性。If your class defines many events that are infrequently raised, you will want to implement event properties. 如需詳細資訊,請參閱如何:使用事件屬性處理多個事件For more information, see How to: Handle Multiple Events Using Event Properties.

標題Title 描述Description
操作說明:引發和使用事件How to: Raise and Consume Events 包含引發和使用事件的範例。Contains examples of raising and consuming events.
操作說明:使用事件屬性處理多個事件How to: Handle Multiple Events Using Event Properties 示範如何使用事件屬性處理多個事件。Shows how to use event properties to handle multiple events.
觀察者設計模式Observer Design Pattern 描述設計模式,可讓訂閱者向提供者註冊,並且接收通知。Describes the design pattern that enables a subscriber to register with, and receive notifications from, a provider.
操作說明:使用 Web Form 應用程式中的事件How to: Consume Events in a Web Forms Application 示範如何處理 Web Form 控制項所引發的事件。Shows how to handle an event that is raised by a Web Forms control.

請參閱See also