1 初步了解事件
定义
·单词Event,译为“事件”
·通顺的解释就是“能够发生什么事情”
角色
·使对象或类具备通知能力的成员
使用
·用于对象或类间的动作协调与信息传递(消息推送)
原理
·“发生->响应”中的5个动作(1)我有一个事件->(2)一个人或着一群人关心我的这个事件->(3)我的这个事件发生了->(4)关心这个事件的人会被依次通知到->(5)被通知到的人根据拿到的信息(又称“事件数据”、“事件参数”、“通知”)对事件进行响应(又称“处理事件”)。
2 事件的应用
事件模型的五个组成部分
1. 事件的拥有者(event source,对象)
·事件作为一个成员,必定存在一个拥有者
·事件不会主动发生,一定是被拥有者的某些内部逻辑触发后才会发生(才会发挥通知的作用)
2. 事件成员(event,成员)
3. 事件的响应者(event subscriber,对象)
4. 事件处理器(event handler,成员)——本质上是一个回调方法
5. 事件订阅——把事件处理器与事件关联在一起,本质上是一种以委托类型为基础的“约定”
·当你拿着一个方法去订阅一个事件时,C#编译器会做一个非常严格的类型检查,它不允许你随便拿一个方法去订阅事件;C#规定:用于订阅事件的事件处理器必须和事件遵守同一个约定(这个约定既约束了事件能够把什么样的消息发送给事件处理器,也约束事件处理器可以处理什么样的消息),如果事件是使用某个约定定义的,而且事件处理器也遵循同样的约定,那么就可以说事件处理器与事件是匹配的,就可以拿事件处理器去订阅这个事件。
·这个“约定”就是“委托”
注意
·事件处理器是方法成员
·挂接事件处理器的时候,可以使用委托实例,也可以直接使用方法名,这是个“语法糖“
·事件处理器对事件的订阅不是随意的,匹配与否由声明事件时所使用的委托类型来检测
·事件可以同步调用也可以异步调用
几种常见的组合方法
例一
·一个事件有一个事件处理器
·一个事件有两个事件处理器
·结果(每隔一秒钟同时在屏幕上打印出Jump!和Sing!)
例二
·组合方式:事件的拥有者和事件的响应者是完全不同的两个对象(⭐)(⭐代表推荐使用的程度)
·代码
例三
·组合:事件的拥有者和事件的响应者是同一个对象(⭐⭐)一个对象拿着自己的方法去订阅、处理自己的事件
·代码
例四
·组合:(应用最广泛的组合方式)事件的拥有者是事件响应者的字段成员(⭐⭐⭐)
·代码1
·代码2(可视化编程)
总结
一个事件可以挂接多个事件处理器(挂接的方式多种多样),一个事件处理器也可以被多个事件所挂接。
·挂接方式1(较传统)
·挂接方式2
·挂接方式3(WindowsForm)
·挂接方式4(WPF)
3 事件的声明
事件的声明(声明自定义的事件)
·完整声明
·简略声明(字段式声明,field-like)
将上面声明事件的代码删除,替换为:
还要修改这一部分:
4 一些疑问
有了委托字段/属性,为什么还需要事件?
·真正的委托类型的字段
·为了程序的逻辑更加“有道理”、更加安全,谨防“借刀杀人”
·事件只能用在+=或者-=左边(要么为事件添加事件处理器,要么为事件移除事件处理器),不能在事件拥有者的外部随便触发这个事件
所以事件本质是委托字段的一个包装器
·这个包装器对委托字段的访问起限制作用,相当于一个“蒙板”(事件只允许外部访问它的+=和-=操作,外界只为这个事件添加事件处理器或者移除事件处理器就够了)
·封装(encapsulation)的一个重要功能就是隐藏
·事件对外界隐藏了委托实例的大部分功能,仅暴露添加/移除事件处理器的功能
·添加/移除事件处理器的时候可以直接使用方法名,这是委托实例所不具备的功能
用于声明事件的委托类型的命名约定
·用于声明Foo事件的委托,一般命名为FooEventHandler(除非是一个非常通用的事件约束)
·FooEventHandler委托的参数一般有两个(由Win32 API演化而来,历史悠久)
·第一个是object类型,名字为sender,实际上就是事件的拥有者、事件的source
·第二个是EventArgs类的派生类,类名一般为FooEventArgs,参数名为e。也就是前面讲过的事件参数
·虽然没有官方的说法,但我们可以把委托的参数列表看做是事件发生后发送给事件响应者的“事件消息”
·触发Foo事件的方法一般命名为OnFoo,即“因何引发”、“事出有因”
·事件的触发必须由事件的拥有者自己来做
·访问级别为protected,不能为public,不然又成了可以“借刀杀人”
事件的命名约定
·带有事态的动词或者动词短语
·事件拥有者“正在做”什么事情,用进行时;事件拥有者“做完了”什么事情,用完成时
5 问题辨析-事件与委托的关系
事件真的是“以特殊方式声明的委托字段/实例”吗?
·不是!只是声明的时候“看起来像”(对比委托字段与事件的简化声明,field-like)
·事件声明的时候使用了委托类型,简化声明造成事件看上去像一个委托字段(实例),而event关键字则更像是一个修饰符——这就是错觉的来源之一
·订阅事件的时候+=操作符后面可以是一个委托实例,这与委托实例的赋值方法语法相同,这也让事件看起来像是一个委托字段——这是错觉的又一来源
·重申:事件的本质是加装在委托字段上的一个“蒙板”(mask),是一个起掩蔽作用的包装器。这个用于阻挡非法操作的“蒙板”绝不是委托字段本身
为什么要使用委托类型来声明事件?
·站在source的角度来看,是为了表明source能对外传递哪些消息
·站在subscriber的角度来看,它是一种约定,是为了约束能够使用什么样签名的方法来处理(响应)事件
·委托类型的实例将用于存储(引用)事件处理器
对比事件与属性
·属性不是字段——很多时候属性是字段的包装器,这个包装器用来保护字段不被滥用
·事件不是委托字段——他是委托字段的包装器,这个包装器用来保护委托字段不被滥用
·包装器永远都不可能是被包装的东西