第11章 事件
如果类型定义了事件成员,那么类型就可以通知其他对象发生了特定的事情。
例如,Button类提供了一个名为Click的事件。应用程序中的一个或多个对象可能想接收关于这个事件的通知,以便在Button被单击后采取某些操作。事件是实现这种交互的类型成员。
具体的说,如果定义一个事件成员,意味着类型要提供以下能力:
- 方法可登记它对该事件的关注。
- 方法可注销它对该事件的关注。
- 该事件发生时,登记了的方法会收到通知。
类型之所以能提供事件通知功能,是因为类型维护了一个已登记方法的列表。事件发生后,类型将通知列表中所有已登记的方法。
CLR的事件模型建立在委托基础上的。委托是调用回调方法的一种类型安全的方式。对象凭借回调方法接收它们订阅的通知。
为了帮助你完全理解事件在CLR中的工作机制,先来描述一个事件很有用的场景。假定现在要设计一个电子邮件应用程序。电子邮件到达时,用户可能希望将该邮件转发给传真机。建构这个应用程序时,假定先设计了一个名为MailManager的类型,它负责接收传入的电子邮件。MailManager类型公开了一个名为NewMail的事件。其他类型(如Fax和Pager)的对象登记它们对这个事件的关注。MailManager收到一封新电子邮件时,会引发该事件。造成邮件分发给每一个已登记的对象。每个对象都用它们自己的方式处理该邮件。
应用程序初始化时,让我们只实例化一个MailManager实例。然后,应用程序可实例化任意数量的Fax和Pager对象。
11.1设计要公开事件的类型
MailManager示例应用程序展示了MailManager类型,Fax类型和Pager类型的所有源代码。
11.1.1第一步:定义类型来容纳所有需要发送给事件通知接收者的附加信息
事件引发时,引发事件的对象可能希望向接收事件通知的对象传递一些附加的信息。这些附加的信息需要封装到它自己的类中,该类通常包含一组私有字段,以及一些用于公开这些字段的只读公共属性。根据约定,这种类应该从EventArgs类派生,并且类名必须以EventArgs结束。
定义一个没有附加信息需要传递的事件时,可直接使用EventArgs.Empty,不用构造一个新的EventArg对象。
//第一步:定义一个类型来容纳所有应该发给事件通知接收者的附加信息
internal class NewMailEventArgs : EventArgs
{
private readonly String m_from, m_to, m_subject;
public NewMailEventArgs(String from, String to, String subject)
{
m_from = from; m_to = to; m_subject = subject;
}
public String From { get { return m_from; } }
public String To { get { return m_to; } }
public String Subject { get { return m_subject; } }
}
//后续的将在MailManager类中进行
internal class MailManager { }
11.1.2第二步:定义事件成员
internal class MailManager
{
public event EventHandler<NewMailEventArgs> NewMail;
void MethodName(object sender, NewMailEventArgs e);
}
事件成员使用C#关键字event来定义。每个事件成员都要指定以下内容:
- 一个可访问性标识符-public。
- 一个委托类型,它指出要调用的方法的原型- EventHandler<NewMailEventArgs>。
- 一个事件名称-NewMail
事件成员的类型是EventHandler<NewMailEventArgs>,意味着“事件通知”的所有接收者都必须提供一个原型和EventHandler<NewMailEventArgs>委托类型匹配的回调方法。
由于泛型EventHandler委托类型的定义如下:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
所以方法原型必须具有以下形式:
void MethodName(object sender, NewMailEventArgs e);
11.1.3 第三步:定义负责引发事件的方法来通知事件的登记对象
。。。