完整定义一个事件的方法
以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 delegate void MailMsgEventHandler(Object sender, MailMsgEventArgs args); - 定义一个事件成员。
public event MailMsgEventHandler MailMsg;
- 定义一个受保护的虚方法,负责通知事件的登记对象。
protected virtual void OnMailMsg(MailMsgEventArgs e)
{
if(MailMsg != null )
{
MailMsg(this , e);
}
} - 定义一个方法,将输入转化为期望的事件。
public void SimulateArrivingMsg(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);
}
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);
{
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);
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方法,我们应该在其内部注销其登记的所有事件。