.NET事件

1 事件简介

1.1 事件介绍

和委托类似,事件是后期绑定机制。 事件基于委托模型,委托模型遵守观察者设计模式,使订阅者能够向提供方注册并接收相关通知。事件发送方推送事件发生的通知,事件接收器接收该通知并定义对它的响应。

事件是对象用于(向系统中的所有相关组件)广播已发生事情的一种方式。 任何其他组件都可以订阅事件,并在事件引发时得到通知。

1.2 事件的设计目标

事件的设计应该遵循下面的目标:

  1. 在事件源和事件接收器之间启用非常小的耦合。 这两个组件可能不会由同一个组织编写,甚至可能会通过完全不同的计划进行更新。
  2. 订阅事件并从同一事件取消订阅应该非常简单。
  3. 事件源应支持多个事件订阅服务器。 它还应支持不依附任何事件订阅服务器。

2 常见的事件使用

2.1 事件委托签名

1 .NET事件委托的标准签名是:

void OnEventRaised(object sender, EventArgs args);

返回类型为 void。 事件基于委托,而且是多播委托。 对任何事件源都支持多个订阅服务器。 来自方法的单个返回值不会扩展到多个事件订阅服务器。

参数列表包含两种参数:发件人和事件参数。

sender的编译时类型为System.Object,即使有一个始终正确的更底层派生的类型亦是如此。 按照惯例使用object。

第二种参数通常是派生自System.EventArgs的类型。即使事件类型无需任何其他参数,你仍将提供这两种参数。应使用特殊值EventArgs.Empty来表示事件不包含任何附加信息。

2.2 定义并引用事件

要将事件添加到类,最简单的方式是将该事件声明为公共字段,例如:

public event EventHandler<FileFoundArgs> FileFound;

看起来它像在声明一个公共字段,这似乎是一个面向对象的不良实践。 你希望通过属性或方法来保护数据访问。 尽管这看起来像一次不良实践,但通过编译器生成的代码却创建了包装器,使事件对象仅能以安全的方式进行访问。 类似字段的事件上唯一可用的操作是(+=)添加处理程序和(-=)删除操作。

2.3 C#使用事件

  • 使用event关键字定义事件:
public event EventHandler<FileListArgs> Progress;
  • 使用委托调用语法调用事件处理程序
Progress?.Invoke(this, new FileListArgs(file));
  • 通过使用+=运算符订阅事件,处理程序方法通常为前缀“On”,后跟事件名称
EventHandler<FileListArgs> onProgress = (sender, eventArgs) =>
    Console.WriteLine(eventArgs.FoundFile);
lister.Progress += OnProgress;
  • 使用 -= 运算符取消订阅
lister.Progress -= onProgress;

3 使用事件和委托的区别

3.1 相同的地方

它们都提供了一个后期绑定方案:在该方案中,组件通过调用仅在运行时识别的方法进行通信。

它们都支持单个和多个订阅服务器方法。 这称为单播和多播支持。

二者均支持用于添加和删除处理程序的类似语法。

最后,引发事件和调用委托使用完全相同的方法调用语法。 它们甚至都支持与 ?. 运算符一起使用的相同的 Invoke() 方法语法。

3.2 不同的地方

1 事件的侦听是可选的

在确定要使用的语言功能时,最重要的考虑因素为是否必须具有附加的订阅服务器。 如果你的代码必须调用由订阅服务器提供的代码,则应使用基于委托的设计。 如果你的代码在不调用任何订阅服务器的情况下可完成其所有工作,则应使用基于事件的设计。

2 事件返回值需要委托

用于事件的委托均具有无效的返回类型

3 事件侦听器通常具有较长的生存期

订阅事件后,事件源可能会在程序的整个生存期内引发事件。(当不再需要事件时,可以取消订阅事件。)