CLR Via C# 3rd 阅读摘要 -- Chapter 11 - Events
Designing a Type That Exposes an Event
Step #1:定义一个类型用来持有将要发送给事件通知接受者的额外信息。从System.EventArgs继承,EventArgs.Empty;
Setp #2:定义一个事件成员;
public event EventHandler<NewMailEventArgs> NewMail;
Step #3:定义一个方法引发事件以通知注册的对象事件以及发生;
protected virtual void OnNewMail(NewMailEventArgs e) {
EventHandler<NewMailEventArgs> temp = Interlocked.CompareExchange(ref NewMail, null, null);
if (temp != null) temp(this, e);
}
如何线程安全的引发事件?
1. 下面这段代码的temp可能会被编译器优化掉;
EventHandler<NewMailEventArgs> temp = NewMail;
if (temp != null) temp(this, e);
2. 下面这段代码是臆想,因为VolatileRead没有一个泛型的重载;
EventHandler<NewMailEventArgs> temp = Thread.VolatileRead(re NewMail);
...
3. 最后一种方法就是Step #2中的那段,但是实际应用时用1.的代码也可行。总之要注意线程的竞争条件。
Step #4:定义一个方法将输入转化到期望的事件;
public void Xyz(...) {
...
NewMailEventArgs e = new NewMailEventArgs(from ,to, subject);
OnNewMail(e);
...
}
How the Complier Implements an Event
1. 一个PRIVATE的初始化成null的委托字段;
private EventHandler<NewMailEventArgs> NewMail = null;
2. 一个PUBLIC的add_Xyz的方法(Xyz就是事件名字)。Delegate.Combine;
public void add_NewMail(EventHandler<NewMailEventArgs> value)
3. 一个PUBLIC的remove_Xyz的方法。Delegate.Remove;
public void remove_NewMail(EventHandler<NewMailEventArgs> value)
注意:
- Delegate.Remove一个从未Add过的方法时,啥也不做,并不会抛出异常啥的。
- Delegate.Add和Remove采用Interlocked Anything模式来保证线程安全。
- 事件成员可以定义成static和virtual的。
Designing a Type That Listens for an Event
1. C#要求你使用+=和-=操作符在列表中add和remove委托。如果显示的调用add和remove方法,编译器会报错:CS0571。
Explicitly Implementing an Event
1. 使用一个字典存储事件标示符(键)和委托链(值);
2. System.ComponentModel.EventHandlerList。
本章小结
CLR的事件模型是基于委托的,是一种特殊的委托,委托就是类型安全的回调函数。描述了如何在类型中暴露事件的步骤以及如何侦听事件,对比了如何安全的触发事件的不同方式。然后分析了编译器如何编译事件。 最后设计了一个EventSet来显示的实现事件以节约资源。