处理和引发事件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 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.

若要定义一个事件,可以在事件类签名中使用 event(在 C# 中)或 Event(在 Visual Basic 中)关键字,并指定事件的委托类型。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.

通常,为了引发事件,您可以在 C# 中添加一个标记为 protectedvirtual 或在 Visual Basic 中标记为 ProtectedOverridable 的方法。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. 因此,委托等同于类型安全函数指针或回叫。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. 在声明中使用 delegate(在 C# 中)和 Delegate(在 Visual Basic 中)关键字标记委托。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. 有关详细信息,请查看 Visual Basic 中的事件和 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.

TitleTitle 说明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 窗体应用程序中使用事件How to: Consume Events in a Web Forms Application 演示如何处理 Web 窗体控件引发的事件。Shows how to handle an event that is raised by a Web Forms control.

请参阅See also