Microsoft.Net框架程序设计学习笔记(15):事件之原理分析
时间:2011-03-27 来源:辛勤的代码工
完整定义一个事件的方法
以Mail发送为例:
- 定义一个类型用于保存所有需要发送给事件通知接受者的附加信息。
按.Net框架约定,所有保存事件信息的类型都应该继承自System.EventArgs,且类型的名称应该EventArgs结束。
public class MailMsgEventArgs : EventArgs
{
public readonly string from, to, subject, body;
public MailMsgEventArgs(string from, string to, string subject, string body)
{
this.from = from;
this.to = to;
this.subject = subject;
this.body = body;
}
} - 定义一个委托类型,用于指定事件触发时被调用的方法原型。
按.Net框架约定,委托类型名称应该以EventHandler结束。另外,回调方法的原型应该有一个void返回值,并且接受两个参数。第1个参数为Object类型,指向发送通知的对象。第2个参数为一个继承自EventArgs的类型,其中包括所有通知接受者需要的附加信息。如果我们定义的事件没有需要传给事件接受者的附加信息,便不必定义新的委托类型。直接使用FCL中的System.EventHandler,并将EventArgs.Empty传递给第二个参数即可。EventHandler的原型为:public delegate void EventHandler(object sender, EventArgs e);public deletegate void MailMsgEventHandler(Object sender, MailMsgEventArgs args);
- 定义一个事件成员。
public event MailMsgEventHandler MailMsg;
- 定义一个受保护的虚方法,负责通知事件的登记对象。
protected virtual void OnMailMsg(MailMsgEventArgs e)
{
if(MailMsg != null)
{
MailMsg(this, e);
}
} - 定义一个方法,将输入转化为期望的事件。
public voidSimulateArrivingMsg(string from, string to, string subject, string body)
{
MailMsgEventArgs e = new MailMsgEventArgs(from, to, subject, body);
OnMailMsg(e);
}
事件原理分析
现在让我们看看定义MailMsg事件时发生了些什么事情?
编译器在遇到事件定义语句:public event MailMsgEventHandler MailMsg会将这段代码翻译成以下3个构造:
//1.一个被初始化为null的私有委托类型字段
private MailMsgEventHandler MailMsg = null;
//2.一个允许对象登记事件的公有add_*方法
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public void add_MailMsg(MailMsgEventHandler handler)
{
MailMsg = (MailMsgEventHandler)Delegate.Combine(MailMsg, handler);
}
//3.一个允许对象注销事件的公有remove_*方法
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public void remove_MailMsg(MailMsgEventHandler handler)
{
MailMsg = (MailMsgEventHandler)Delegate.Remove(MailMsg, handler);
}
第1步是构造一个委托类型的字段,该字段引用的是一个委托链表的首部,链表中包含了那些期望在事件发生时被通知的委托对象。当一个侦听者需要登记事件时,它只需将一个委托实例添加到委托链表上就可以了。
注意:登记和注销事件的方法应用了MethodImplAttribute特性,这个特性使方法被标识为同步方法,这使得它们得以实现线程安全,也就是说多个侦听者可以同时登记或注销事件,而不损坏委托链表。
侦听事件
登记、注销事件代码示例:
class Fax
{
public Fax(MailManager mm)
{
mm.MailMsg += new MailManager.MailMsgEventHandler(FaxMsg); //编译器将该代码转换为: //mm.add_MailMsg(new MailManager.MailMsgEventHandler(FaxMsg)); }
private void FaxMsg(object sender, MailManager.MailMsgEventArgs e)
{
........
}
public void Unregister(MailManager mm)
{
mm.MailMsg -= new MailManager.MailMsgEventHandler(FaxMsg); //编译器将该代码转换为: //mm.remove_MailMsg(new MailManager.MailMsgEventHandler(FaxMsg));
}
}
当一个对象不再希望接受事件通知时,应该注销该事件。
注意:只要一个对象仍然登记有另一个对象的事件,该对象就不可能被执行垃圾收集。如果我们的类型实现了IDisposable接口的Dispose方法,我们应该在其内部注销其登记的所有事件。
相关阅读 更多 +