文章目录
15.1 发布者和订阅者
发布者 / 订阅者模式:发布者定义了一系列事件,订阅者可以通过向发布者提供一个方法来“注册”以获取通知。
当事件发生时,发布者“触发事件”,然后执行订阅者提交的所有方法。
![image-20231103180517931](https://i-blog.csdnimg.cn/blog_migrate/8cfcf2096df4a276fd53a996ebdc0f6d.png#pic_center)
- 发布者:发布某个事件的类或结构,其他类可以在该事件发生时得到通知。
- 订阅者:注册事件,在事件发生时得到通知的类或结构。
![image-20231103180753626](https://i-blog.csdnimg.cn/blog_migrate/c3ff945a35089324aabc35bf0bf0190e.png#pic_center)
- 事件提供私有委托的结构化访问,你无法直接访问委托。
- 对事件只能进行添加、删除或调用操作。
- 事件被触发时会调用委托来依次执行列表中的方法。
![image-20231103181031956](https://i-blog.csdnimg.cn/blog_migrate/12fd7c9df3e5cb94a689abd0048c6ffe.png#pic_center)
15.2 源代码组件概览
需要在事件中使用的代码有以下 5 部分:
- 委托类型声明。
- 事件处理程序声明。
- 事件声明。
- 事件注册。
- 触发事件的代码。
![image-20231103181440334](https://i-blog.csdnimg.cn/blog_migrate/6fc01c68cb70662c5e4324f29027a46a.png#pic_center)
15.3 声明事件
- 事件声明在一个类中。
- 需要委托类型的名称。
- 声明为 public 以供外部订阅者访问。
- 不能使用 new 创建事件对象。
![image-20231103181639903](https://i-blog.csdnimg.cn/blog_migrate/2c1ab8fbbae85058c959845414a9d0e2.png#pic_center)
事件是成员,并不是类型:
- 不能在一段可执行代码中声明事件。
- 必须声明在类或结构中。
- 事件成员被隐式自动初始化为 null。
- BCL 声明了名为 EventHandler 的委托,专门用于系统事件。
15.4 订阅事件
- 使用
+=
运算符为事件添加事件处理程序。 - 事件处理程序可以是以下任意一种:
- 实例方法。
- 静态方法。
- 匿名方法。
- Lambda 表达式。
![image-20231103182013844](https://i-blog.csdnimg.cn/blog_migrate/43c12a31e2e8900d9489d242ada4f369.png#pic_center)
![image-20231103182116181](https://i-blog.csdnimg.cn/blog_migrate/bcc1b10af8a20da1421cb8037a138e46.png#pic_center)
15.5 触发事件
- 触发事件前与 null 比较。
- 和调用方法一样使用事件。
![image-20231103182555769](https://i-blog.csdnimg.cn/blog_migrate/fb8df80d83dd7c330073a2fc45d5a19c.png#pic_center)
以下示例展示发布者 Incrementer 和订阅者 Dozens 的使用,在 DoCount 方法中,每增加 12 个计数就触发 CountedADozen 事件。
![image-20231103182745213](https://i-blog.csdnimg.cn/blog_migrate/5d3b4e640a3bd71bcbdfd8dc0579171c.png#pic_center)
![image-20231103182826555](https://i-blog.csdnimg.cn/blog_migrate/5007831717d15bbc27d63afe3e143009.png#pic_center)
15.6 标准事件的用法
Windows GUI 编程广泛使用事件,EventHandler 委托类型的声明如下:
![image-20231103183104166](https://i-blog.csdnimg.cn/blog_migrate/16d99408e02029d3901ae1473cbc82ea.png#pic_center)
- 第一个参数用来保存触发事件对象的引用。
- 第二个参数用来保存状态信息。
- EventArgs 不能传递任何数据,通常会被忽略。
- 如果需要传递数据,需要对 EventArgs 进行派生,用合适字段存储数据。
- object 和 EventArgs 总是传入参数的基类,因此可以用于处理所有事件。
15.6.1 通过扩展 EventArgs 来传递数据
自定义如下信息类,类的名称应以 EventArgs 结尾:
![image-20231103183843250](https://i-blog.csdnimg.cn/blog_migrate/a8250fe2b1949891eb815da55f8c76b5.png#pic_center)
![image-20231103183828505](https://i-blog.csdnimg.cn/blog_migrate/e78cf8e9257648c5c7699b876561193a.png#pic_center)
使用带泛型的委托以使用自定义信息类:
![image-20231103184021031](https://i-blog.csdnimg.cn/blog_migrate/45030facb2eb63abad1514fd4189a257.png#pic_center)
![image-20231103184201741](https://i-blog.csdnimg.cn/blog_migrate/ce3fc6f6a9f16b105524e0b8ae05add2.png#pic_center)
15.6.2 移除事件处理程序
使用 -=
运算符移除事件处理程序。
和委托类似,如果一个处理程序在事件内注册了多次,移除时只移除列表最后一个。
15.7 事件访问器
使用 add
和 remove
访问器可以修改 +=
和 -=
运算符的行为。
- 声明事件访问器看上去和声明属性类似。
- 声明事件访问器后,事件不包含内嵌委托,需要自己实现存储和移除事件的方法。
- 事件访问器没有返回值,为 void 方法。
![image-20231103184600692](https://i-blog.csdnimg.cn/blog_migrate/f7be770924f3a5a414ffeaeccd949d2f.png#pic_center)