C# - 事件
事件是对象发出的通知,用于表示某个操作的发生。C# 中的事件遵循观察者设计模式。
引发事件的类称为发布者,接收通知的类称为订阅者。单个事件可以有多个订阅者。通常,当某个操作发生时,发布者会引发一个事件。对接收操作发生通知感兴趣的订阅者应该注册事件并处理它。
在 C# 中,事件是委托的封装。它依赖于委托。委托定义了订阅者类的事件处理方法的签名。
下图说明了 C# 中的事件。

声明事件
事件可以分两步声明
- 声明一个委托。
- 使用 `event` 关键字声明委托的一个变量。
以下示例演示了如何在发布者类中声明事件。
public delegate void Notify(); // delegate
public class ProcessBusinessLogic
{
public event Notify ProcessCompleted; // event
}
在上面的示例中,我们声明了一个委托 `Notify`,然后在使用 `ProcessBusinessLogic` 类中的 "event" 关键字声明了一个 `Notify` 委托类型的事件 `ProcessCompleted`。因此,`ProcessBusinessLogic` 类被称为发布者。`Notify` 委托指定了 `ProcessCompleted` 事件处理程序的签名。它指定订阅者类中的事件处理方法必须具有 void 返回类型且没有参数。
现在,让我们看看如何引发 `ProcessCompleted` 事件。考虑以下实现。
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
`OnProcessCompleted()` 方法使用 `ProcessCompleted?.Invoke();` 调用委托。这将调用所有注册到 `ProcessCompleted` 事件的事件处理方法。
订阅者类必须注册到 `ProcessCompleted` 事件并使用其签名与 `Notify` 委托匹配的方法处理它,如下所示。
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` 委托的签名匹配。
内置 EventHandler 委托
.NET Framework 包含内置委托类型 EventHandler 和 EventHandler<TEventArgs>,用于最常见的事件。通常,任何事件都应包含两个参数:事件源和事件数据。对于不包含事件数据的所有事件,请使用 `EventHandler` 委托。对于包含要发送给处理程序的数据的事件,请使用 EventHandler<TEventArgs> 委托。
上面显示的示例可以使用 `EventHandler` 委托而无需声明自定义 `Notify` 委托,如下所示。
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()` 方法包含两个与 EventHandler 委托匹配的参数。此外,当我们使用 `OnProcessCompleted()` 方法中的 `Invoke()` 引发事件时,将 `this` 作为发送者和 `EventArgs.Empty` 传递。因为我们的事件不需要任何数据,它只是通知订阅者进程完成,所以我们传递了 `EventArgs.Empty`。
传递事件数据
大多数事件都会向订阅者发送一些数据。EventArgs 类是所有事件数据类的基类。.NET 包含许多内置事件数据类,例如 SerialDataReceivedEventArgs。它遵循所有事件数据类以 EventArgs 结尾的命名模式。您可以通过派生 EventArgs 类来创建自定义事件数据类。
使用 EventHandler<TEventArgs> 将数据传递给处理程序,如下所示。
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 基类的类,如下所示。
class ProcessEventArgs : EventArgs
{
public bool IsSuccessful { get; set; }
public DateTime CompletionTime { get; set; }
}
以下示例演示了如何将自定义 `ProcessEventArgs` 类传递给处理程序。
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# 中创建、引发、注册和处理事件。
了解 C# 中委托和事件的区别。
- 事件是委托的包装器。它依赖于委托。
- 使用“event”关键字与委托类型变量一起声明事件。
- 对于常见事件,使用内置委托 EventHandler 或 EventHandler<TEventArgs>。
- 发布者类引发事件,订阅者类注册事件并提供事件处理方法。
- 将引发事件的方法命名为以“On”前缀加上事件名称。
- 处理程序方法的签名必须与委托签名匹配。
- 使用 += 运算符注册事件。使用 -= 运算符取消订阅。不能使用 = 运算符。
- 使用 EventHandler<TEventArgs> 传递事件数据。
- 派生 EventArgs 基类以创建自定义事件数据类。
- 事件可以声明为静态、虚拟、密封和抽象。
- 接口可以包含事件作为成员。
- 如果有多个订阅者,事件处理程序将同步调用。