本文摘自PHP中文网,作者PHPzhong,侵删。
文件涉及的内容:设计公开事件类型
编译器如何实现事件
设计侦听事件的类型
显式实现事件
事件:定义了事件成员的类型允许类型通知其他对象发生特定的事情。
CLR事件模型以委托为基础,委托是调用回调方法的一种类型安全的方式,对象凭借调用方法接收他们订阅的通知。
定义了事件成员的类型要求能够提供以下功能:
方法能登记它对事件的关注
方法能注销它对事件的关注
事件发生时,登记的方法将收到通知
本文章以一个电子邮件应用程序为例。当电子邮件到达时,用户希望将邮件转发给传真机或寻呼机进行处理。先设计MainlManager类型来接收传入的电子邮件,它公开NewMain事件。其他类型(Fax或Pager)对象登记对于该事件的关注。MailManager收到新电子邮件会引发该事件,造成邮件分发给每个已登记的对象,它们都有自己的方式处理邮件。
1.1设计要公开事件的类型
第一步:定义类型来容纳所有需要发送给事件通知接收者的附加信息
该类型通常包含一组私有字段以及一些用于公开这些字段的只读公共属性。
1 |
|
第二步:定义事件成员
1 2 3 |
|
其中NewMail是事件名称。事件成员类型是EventHandler<NewMailEventArgs>说明事件通知的所有接收者都必须提供一个原型和其委托类型匹配的回调方法。由于泛型System.EventHandler委托类型的定义如下:
public delegate void EventHandler<TEventArgs>(Object sender,TEventArgs e);
所以方法原型必须具有以下形式:void MethodName(Object sender,NewMailEventArgs e);之所以事件模式要求所有事件处理程序的返回类型都是void,是因为引发事件后可能要调用好几个回调方法,但没办法获得所有方法的返回值,返回void就不允许回调方法有返回值。
第三步:定义负责引发事件的方法来通知事件的登记对象
1 |
|
上面方法使用了Volatile.Read()方法确保线程安全,主要考虑下面两种情况:
1.直接判断NewMail!=null,但在调用NewMail之前,另一个线程可能从委托链中移除了一个委托,使其为空,从而发生(NullReferenceException)异常。
2.有些人可能也会将其保存在一个临时变量中,但未使用Volatile,理论上可以但是如果编译器发生优化代码移除该临时变量,那就和第一种情况一样。
使用Volatile.Read会强迫NewMail在这个调用发生时读取,引用必须复制到temp变量中,比较完美的解决方式。但是在单线程的中不会出现这种情况
第四步 定义方法将输入转化为期望事件
1 |
|
该方法指出一封新的邮件已到达MailManager。
1.2 编译器如何实现事件
在MailManager类中我们用一句话定义事件成员本身:public event EventHandler<NewMailEventArgs> NewMail;
C#编译器会转换为以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
本实例中,add和remove方法可访问性都是public是因为事件NewMail声明为public,事件的可访问性决定了什么代码能登记和注销对事件的关注。但无论如何只有类型本身才能访问上述委托字段NewMail。除了上述生成的代码,编译器还会在托管程序集的元数据中生成事件定义记录项。包含一些标志和基础委托类型。CLR本身并不使用这些元数据信息运行时只需要访问器方法。
1.3 设计侦听事件的类型
如何定义一个类型来使用另一个类型提供的事件。以Fax类型为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
电子邮件应用程序初始化时首先构造MailManager对象,并将对该对象的引用保存到变量中。然后构造Fax对象,并将MailManager对象引用作为实参传递。在Fax构造器中,使用+=登记对NewMail事件的关注。
1.4 显式实现事件
对于System.Windows.Forms.Control类型定义了大约70个事件。每个从Control派生类型创建对象都要浪费大量内存,而大多数我们只关心少数几个事件。如何通过显式实现事件来高效的实现提供了大量事件的类思路如下:
定义事件时:公开事件的每个对象都要维护一个集合(如字典)。集合将某种事件标识符作为健,将委托列表作为值。新对象构造时集合也是空白。登记对一个事件的关注会在集合中查找事件的标识符。如果事件标识符存在,新委托就和这个事件的委托列表合并,否则就添加事件标识符和委托。
引发事件时:对象引发事件会在集合中查找事件的标识符,如果没有说明没有对象登记对这个事件的关注,所以也没委托需要回调。否则就调用与它关联的委托列表。
1 |
|
接下来定义类来使用EventSet
1 |
|
如何使用TypeWithLotsOfEvent,只需按照标准的语法向事件登记即可
1 2 |
|
以上就是C#系列文章事件的详细内容!
相关阅读 >>
通达oa 使用C#的socket编程来其替代原有操作的示例代码分享
C#如何使用 oledbconnection 连接读取excel?(代码实例)
更多相关阅读请进入《C#》频道 >>
C#高级编程(第11版) C# 7 & .NET Core 2.0(.NET开发经典名著)
作者:[美]克里斯琴·内格尔(Christian Nagel)著。出版时间:2019年3月。