第十一章 事件
一.
定义了事件成员的类型允许类型(或类型的实例)在某些特定事情发生的时候通知其他对象.意味着类型为我们提供了以下三种能力:
1.允许对象登记该事件
2.允许对象注销该事件
3.允许定义事件的对象维持一个登记对象的集合,并在某些特定的事情发生时通知这些对象
例:当一个电子邮件消息到达时,用户可能希望将该消息转发给一个传真机(Fax)或一个寻呼机(Pager).
我们首先会设计一个名为MailManger的类型负责接受发进来的电子邮件消息.然后再为MailManger类型定义个名为MailMsg的事件.其他类型(如Fax)则可以登记该事件.当MailManger收到一个新的电子邮件消息时,它会触发该事件,将消息分给每一个登记对象.
首先:应用程序通过创建一个MailManger的实例来进行初始化.其次,MailManger提供了一个MailMsg事件.当Fax或Pager对象被构造时,它们将自己登记到MailManger的MailMsg事件上,这样在新的电子邮件消息到达的时候,MailManger将知道要通知Fax和Pager对象.当MailManger收到一个新的电子消息时,它会触发MailMsg事件,从而使所有的登记对象都有机会以它们期望的方式来处理新消息.
代码如下:
定义一个MailManger类

Code
1
class MailManger
2
{
3
//在MailManger内部定义MailMsgEventArgs类型
4
public class MailMsgEventArgs : EventArgs
5
{
6
public readonly string from, to, subject, body;
7
//1.传递给事件接受者的类型定义信息
8
public MailMsgEventArgs(string from,string to,string subject,string body)
9
{
10
this.from = from;
11
this.to = to;
12
this.subject = subject;
13
this.body = body;
14
}
15
}
16
17
//2.下面的委托类型定义了接受者必须实现的回调方法原型
18
public delegate void MailMsgEventHandler(object sender,MailMsgEventArgs args);
19
20
//3.事件成员
21
public event MailMsgEventHandler MailMsg;
22
23
//4.下面的收保护虚方法负责通知事件的登记对象
24
protected virtual void OnMailMsg(MailMsgEventArgs e)
25
{
26
//有对象登记事件了没?
27
if (MailMsg != null)
28
{
29
//如果有,则通知委托链表上的所有对象
30
MailMsg(this,e);
31
}
32
}
33
34
//5.下面的方法将输入转化为期望的事件,该方法
35
//在新的电子邮件消息到达时被调用
36
public void SimulateArrivingMsg(string from,string to,string subject,string body)
37
{
38
//构造一个对象保存希望传递给通知接受者的信息
39
MailMsgEventArgs e = new MailMsgEventArgs(from,to,subject,body);
40
41
//调用虚方法通知对象已发生,
42
//如果派生类型没有重写该虚方法,
43
//对象将通知所有登记的事件侦听者
44
OnMailMsg(e);
45
46
}
47
}
对于上面代码中标记的数字解释,我们进行分析:
1,定义一个类型用于保存所有需要发送给事件通知接受者的附加信息
注意:EventArgs类型在FCL中定义如下:

Code
1
[Serializable]
2
[ComVisible(true)]
3
public class EventArgs
4
{
5
// 摘要:
6
// 表示没有事件数据的事件。
7
public static readonly EventArgs Empty;
8
9
// 摘要:
10
// 初始化 System.EventArgs 类的新实例。
11
public EventArgs();
12
}
该类型的实现非常简单,它仅仅是作为一个让其他类型继承的基类型而出现的.许多事件都没有额外的信息需要传递.如,当一个Button通知它的登记接受者自己被按下时,简单地调用回调方法就已经足够了。当我们定义个不需要传递任何额外数据的事件时,可以直接使用EventArgs.Empty,而不用再构造新的EventArgs对象.
2.定义一个委托类型,用于指定事件触发时被调用的方法原型.
方法接受两个参数,第一个为Object类型,其指向发送通知的对象.第二个参数为一个继承自EventArgs的类型,其中包含所有通知接受者需要的附加信息.
如果我们定义的事件没有需要传递给事件接受者的附加信息,我们便不必定义新的委托类型,直接使用FCL中的System.EventHandler,并将EventArgs.Empty传递给第二个参数即可.
EventHandler的原型如下:

Code
1
// 摘要:
2
// 表示将处理不包含事件数据的事件的方法。
3
[Serializable]
4
[ComVisible(true)]
5
public delegate void EventHandler(object sender, EventArgs e);
3.定义一个事件成员
事件的名称为MailMsg.该事件的类型为MailMsgEventHandler,其含义为所有事件通知的接受者都必须提供一个原型和MailMsgEventHandler相匹配的回调方法.
4.定义一个受保护的虚方法,负责通知事件的登记对象
当一个新的电子邮件信息到达时,OnMailMsg方法会被调用,该方法接受一个经过初始化的MailMsgEventArgs对象(其中包含事件的附加信息).该方法应该首先检查是否有对象登记了事件,如果有,则触发事件.
5.定义了一个方法.将输入转化为期望的事件
二:该是展示怎样定义个类型来使用MailManger提供的MailMsg事件的时候了

Code
1
class Fax
2
{
3
//将MailManger 对象传递给构造器
4
public Fax(MailManger mailManger)
5
{
6
//构造一个指向FaxMsg回调方法的MailMsgEventHandler委托实例。
7
//然后登记MailManger的MailMsg事件
8
mailManger.MailMsg += new MailManger.MailMsgEventHandler(FaxMsg);
9
}
10
11
//MailManger将调用该方法来通知Fax对象收到一个新的电子邮件信息
12
private void FaxMsg(object sender,MailManger.MailMsgEventArgs e)
13
{
14
//参数'sender'表示MailManger对象,如果期望和事件的触发者通信,将会用到该参数
15
16
//参数'e'表示MailManger对象希望提供的一些附加事件信息
17
18
//通常情况下,这里的代码应该传真电子邮件信息,
19
//这里的实现仅仅是将消息显示到控制台上
20
Console.WriteLine("传真邮件信息到来");
21
Console.WriteLine("来自:{0},\n到:{1},\n 主题:{2},\n内容:{3}",e.from,e.to,e.subject,e.body);
22
}
23
24
public void Unregister(MailManger mailManger)
25
{
26
//构造一个指向FaxMsg回调方法的MailMsgEventHandler委托实例
27
MailManger.MailMsgEventHandler callback = new MailManger.MailMsgEventHandler(FaxMsg);
28
29
//注销MailManger的MailMsg事件
30
mailManger.MailMsg -= callback;
31
32
}
33
}
其中的:
mailManger.MailMsg += new MailManger.MailMsgEventHandler(FaxMsg);
可以这样理解:
new MailManger.MailMsgEventHandler(FaxMsg)这是构造一个指向FaxMsg回调方法的MailMsgEventHandler委托实例.mailManger.MailMsg 是MailManger的MailMsg事件.它通过委托实例,登记了事件.也可以写成这样:
mailManger.add_MailMsg(new MailManger.MailMsgEventHandler(FaxMsg));
下面是应用程序:

Code
1
class Program
2
{
3
static void Main(string[] args)
4
{
5
MailManger mailManger = new MailManger();
6
//mailManger.SimulateArrivingMsg("dreamersjun", "博客园", "事件的例题", "关于一些事件的分析和总结,感觉很难~!");
7
Fax fax = new Fax(mailManger);
8
9
//到这里才可以触发,只有登记了的事件才可以被通知
10
mailManger.SimulateArrivingMsg("dreamersjun", "博客园", "事件的例题", "关于一些事件的分析和总结,感觉很难~!");
11
12
fax.Unregister(mailManger);
13
}
14
}