第11章 事件
CLR的事件模型建立在委托的基础上。委托是调用回调方法的一种类型安全方式。对象凭借回调方法接收它们订阅的通知。
设计要公开事件的类型有四步:
第一步:定义类型来容纳所有需要发送给事件通知接收者的附加信息
也就是定义一个继承自System.EventArgs的派生类,主要包含一组私有字段和一些公开的只读公共属性。
第二步:定义事件成员
事件成员使用c#关键字event来定义,event其实是一个封装好的delegate,所以才说事件模型是建立在委托的基础上的。
事件成员都要指定以下内容:一个可访问性标识符(几乎肯定是public);一个委托类型;例如:
public event EventHandler<NewMailEventArgs> NewMail;
订阅者都必须提供一个原型和事件成员定义的委托类型匹配的回调方法。
第三步:定义负责引发事件的方法来通知事件的登记对象
发送方应定义一个受保护的虚方法。该方法默认实现只检查一下是否有对象登记了对事件的关注。如果有,就引发事件,从而通知事件的登记对象。例如:
protected virtual void OnNewMail(NewMailEventArgs e)
{
EventHandler<NewMailEventArgs> temp = Interlocked.CompareExchange(ref NewMail, null, null);
if (temp != null) temp(this, e);
}
注:以线程安全的方式引发事件。早前的引发方式只是单纯的检查是否有对象登记了对事件的关注,这存在线程安全的问题,假如一个线程刚获取事件成员,同时另一线程从委托链移除了一个委托,使用事件成员为null,这会造成一个异常。所以现在推荐使用上面的例子的方法,用线程安全的方式引发事件。
第四步:定义方法将输入转化为期望事件
public void SimulateNewMail(string from, string to, string subject)
{
NewMailEventArgs e = new NewMailEventArgs(from, to, subject);
OnNewMail(e);
}
编译器如何实现事件
c#编译器在编译public event EventHandler<NewMailEventArgs> NewMail;这行代码时会生成三个构造:
1.一个私有委托字段。
2.一个公共add_Xxx方法,订阅事件。
3.一个公共remove_Xxx方法,取消订阅。