和委托类似,事件是后期绑定机制。 实际上,事件是建立在对委托的语言支持之上的。
事件是一种对象广播(向系统中所有感兴趣的组件)广播所发生事件的方法。 任何其他组件都可以订阅事件,并在事件发生时收到通知。
你可能在某些编程中使用过事件。 许多图形系统都有一个事件模型来报告用户交互。 这些事件将报告鼠标移动、按钮按下和类似的交互。 这是最常见的情境之一,但并不是使用事件的唯一情境。
可以定义应针对类引发的事件。 使用事件时,需要注意的一点是特定事件可能没有任何注册的对象。 必须编写代码,以便在未配置侦听器时它不会引发事件。
订阅事件还会在两个对象(事件源和事件接收器)之间创建耦合。 需要确保当不再对事件感兴趣时,事件接收器将从事件源取消订阅。
事件支持的设计目标
事件的语言设计面向以下目标:
- 启用事件源与事件接收器之间的最小耦合。 这两个组件可能由不同的组织编写,甚至可能按不同的计划进行更新。
- 订阅事件并从同一事件取消订阅应该很简单。
- 事件源应支持多个事件订阅服务器。 它还应支持不附加任何事件订阅服务器。
可以看到,活动的目标与代表的目标相似。 这就是为什么事件语言支持是基于委托语言支持构建的。
活动的语言支持
用于定义事件以及订阅或取消订阅事件的语法是对委托语法的扩展。
使用 event
关键字定义事件:
public event EventHandler<FileFoundArgs>? FileFound;
事件的类型(EventHandler<FileListArgs>
在此示例中)必须是委托类型。 声明事件时应遵循一些约定。 通常情况下,事件委托类型具有无效的返回。 事件声明应为谓词或谓词短语。 当事件报告发生的情况时,请使用过去的时态。 使用当前时态谓词(例如, Closing
)报告即将发生的情况。 通常,使用现在时表示您的类支持某种自定义行为。 最常见的方案之一是支持取消。 例如,Closing
事件可以包含一个参数,该参数指示关闭操作是否应继续进行。 其他方案使调用方可以通过更新事件参数的属性来修改行为。 可以引发事件以指示算法将采取的下一步作。 事件处理程序可以通过修改事件参数的属性来强制执行其他作。
如果要引发事件,请使用委托调用语法调用事件处理程序:
FileFound?.Invoke(this, new FileFoundArgs(file));
如委托部分中所述,?.
运算符便于确保不尝试在没有事件的订阅服务器时引发该事件。
通过使用 +=
运算符订阅事件:
var fileLister = new FileSearcher();
int filesFound = 0;
EventHandler<FileFoundArgs> onFileFound = (sender, eventArgs) =>
{
Console.WriteLine(eventArgs.FoundFile);
filesFound++;
};
fileLister.FileFound += onFileFound;
处理程序方法通常具有前缀“On”,后跟事件名称,如前面的代码所示。
使用 -=
运算符取消订阅:
fileLister.FileFound -= onFileFound;
必须声明表示事件处理程序的表达式的局部变量。 这将确保取消订阅删除该处理程序。 如果使用的是 lambda 表达式的主体,则将尝试删除从未附加过的处理程序,此操作为无效操作。
在下一篇文章中,你将了解有关典型事件模式的详细信息,以及此示例的不同变体。