C# - 事件
本文关键字:事件 | 更新日期: 2023-09-14 19:55:51
事件是对象发送的通知,用于指示操作的发生。 .NET 中的事件遵循观察者设计模式。
引发事件的类称为发布服务器,接收通知的类称为订阅服务器。单个事件可以有多个订阅者。 通常,发布者会在发生某些操作时引发事件。有兴趣在发生操作时收到通知的订阅者应注册事件并处理它。
在 C# 中,事件是一个封装的delegate。它取决于委托。delegate 定义订阅者类的事件处理程序方法的签名。
下图说明了 C# 中的事件。
Event Publisher & Subscriber
声明事件
可以通过两个步骤声明事件:
- 声明委托。
- 使用
event
关键字声明委托的变量。
下面的示例演示如何在发布者类中声明事件。
示例: Declaring an Event
public delegate void Notify(); // delegate
public class ProcessBusinessLogic
{
public event Notify ProcessCompleted; // event
}
在上面的示例中,我们声明了一个委托Notify
,然后在 ProcessBusinessLogic
类中使用 "event" 关键字声明了一个委托类型 Notify
的事件ProcessCompleted
。
因此,ProcessBusinessLogic
类称为发布服务器。
Notify
委托指定ProcessCompleted
事件处理程序的签名。它指定订阅者类中的事件处理程序方法必须具有 void 返回类型且没有参数。
现在,让我们看看如何引发ProcessCompleted
事件。请考虑以下实现。
示例: Raising an Event
public delegate void Notify(); // delegate
public class ProcessBusinessLogic
{
public event Notify ProcessCompleted; // event
public void StartProcess()
{
Console.WriteLine("Process Started!");
// some code here..
OnProcessCompleted();
}
protected virtual void OnProcessCompleted() //protected virtual method
{
//if ProcessCompleted is not null then call delegate
ProcessCompleted?.Invoke();
}
}
上面,StartProcess()
方法在末尾调用onProcessCompleted()
的方法,从而引发一个事件。
通常,要引发事件,应使用名称 On<EventName>
定义受保护和虚拟方法。
受保护和虚拟使派生类能够重写引发事件的逻辑。但是,派生类应始终调用基类的 On<EventName>
方法,以确保注册的委托接收事件。
OnProcessCompleted()
方法使用 ProcessCompleted?.Invoke();
调用委托。这将调用向ProcessCompleted
事件注册的所有事件处理程序方法。
订阅者类必须注册到ProcessCompleted
事件,并使用其签名与委托匹配的方法处理它Notify
如下所示。
示例: Consume an Event
class Program
{
public static void Main()
{
ProcessBusinessLogic bl = new ProcessBusinessLogic();
bl.ProcessCompleted += bl_ProcessCompleted; // register with an event
bl.StartProcess();
}
// event handler
public static void bl_ProcessCompleted()
{
Console.WriteLine("Process Completed!");
}
}
上面,Program
类是ProcessCompleted
事件的订阅者。
它使用 += 运算符向事件注册。请记住,这与我们在多播委托的调用列表中添加方法的方式相同。
bl_ProcessCompleted()
方法处理事件,因为它与Notify
委托的签名匹配。
内置事件处理程序委托
.NET Framework 包括内置委托类型EventHandler 和EventHandlerEventHandler
委托。对包含要发送到处理程序的数据的事件使用EventHandler委托。
上面显示的示例可以使用EventHandler
委托,而无需声明自定义Notify
委托,如下所示。
示例: EventHandler
class Program
{
public static void Main()
{
ProcessBusinessLogic bl = new ProcessBusinessLogic();
bl.ProcessCompleted += bl_ProcessCompleted; // register with an event
bl.StartProcess();
}
// event handler
public static void bl_ProcessCompleted(object sender, EventArgs e)
{
Console.WriteLine("Process Completed!");
}
}
public class ProcessBusinessLogic
{
// declaring an event using built-in EventHandler
public event EventHandler ProcessCompleted;
public void StartProcess()
{
Console.WriteLine("Process Started!");
// some code here..
OnProcessCompleted(EventArgs.Empty); //No event data
}
protected virtual void OnProcessCompleted(EventArgs e)
{
ProcessCompleted?.Invoke(this, e);
}
}
在上面的示例中,事件处理程序bl_ProcessCompleted()
方法包括两个与事件处理程序委托匹配的参数。
此外,当我们在 OnProcessCompleted()
方法中使用 Invoke()
引发事件时,将this
作为发送者和EventArgs.Empty
传递。
因为我们的事件不需要任何数据,所以它只是通知订阅者该过程的完成,所以我们通过了EventArgs.Empty
。
传递事件数据
大多数事件会向订阅者发送一些数据。EventArgs 类是所有事件数据类的基类。 .NET 包含许多内置的事件数据类,例如 SerialDataReceivedEventArgs。 它遵循使用 EventArgs 结束所有事件数据类的命名模式。可以通过派生 EventArgs 类来为事件数据创建自定义类。
使用EventHandler
示例: Passing Event Data
class Program
{
public static void Main()
{
ProcessBusinessLogic bl = new ProcessBusinessLogic();
bl.ProcessCompleted += bl_ProcessCompleted; // register with an event
bl.StartProcess();
}
// event handler
public static void bl_ProcessCompleted(object sender, bool IsSuccessful)
{
Console.WriteLine("Process " + (IsSuccessful? "Completed Successfully": "failed"));
}
}
public class ProcessBusinessLogic
{
// declaring an event using built-in EventHandler
public event EventHandler<bool> ProcessCompleted;
public void StartProcess()
{
try
{
Console.WriteLine("Process Started!");
// some code here..
OnProcessCompleted(true);
}
catch(Exception ex)
{
OnProcessCompleted(false);
}
}
protected virtual void OnProcessCompleted(bool IsSuccessful)
{
ProcessCompleted?.Invoke(this, IsSuccessful);
}
}
在上面的示例中,我们将单个布尔值传递给指示流程是否成功完成的处理程序。
如果要将多个值作为事件数据传递,请创建一个从 EventArgs 基类派生的类,如下所示。
示例: Custom EventArgs Class
class ProcessEventArgs : EventArgs
{
public bool IsSuccessful { get; set; }
public DateTime CompletionTime { get; set; }
}
下面的示例演示如何将自定义ProcessEventArgs
类传递给处理程序。
示例: Passing Custom EventArgs
class Program
{
public static void Main()
{
ProcessBusinessLogic bl = new ProcessBusinessLogic();
bl.ProcessCompleted += bl_ProcessCompleted; // register with an event
bl.StartProcess();
}
// event handler
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
{
// declaring an event using built-in EventHandler
public event EventHandler<ProcessEventArgs> ProcessCompleted;
public void StartProcess()
{
var data = new ProcessEventArgs();
try
{
Console.WriteLine("Process Started!");
// some code here..
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# 中创建、引发、注册和处理事件。
注意:
- 事件是委托的包装器。这取决于代表。
- 使用带有委托类型变量的"event"关键字来声明事件。
- 对常见事件使用内置委托 EventHandler 或 EventHandler<TEventArgs>。
- 发布者类引发事件,订阅者类注册事件并提供事件处理程序方法。
- 将引发以"On"为前缀的事件的方法命名为事件名称。
- 处理程序方法的签名必须与委托签名匹配。
- 使用 += 运算符注册事件。使用 -= 运算符取消订阅。不能使用 = 运算符。
- 使用 EventHandler<TEventArgs>传递事件数据。
- 派生 EventArgs 基类以创建自定义事件数据类。
- 事件可以声明为静态、虚拟、密封和抽象。
- 接口可以将事件作为成员包含在内。
- 如果有多个订阅者,则会同步调用事件处理程序。