完整定义一个事件的方法

  以Mail发送为例:

  1. 定义一个类型用于保存所有需要发送给事件通知接受者的附加信息。
    按.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;
    }
    }
  2. 定义一个委托类型,用于指定事件触发时被调用的方法原型。
    按.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);
  3. 定义一个事件成员。
    public event MailMsgEventHandler MailMsg;
  4. 定义一个受保护的虚方法,负责通知事件的登记对象。
    protected virtual void OnMailMsg(MailMsgEventArgs e)
    {
    if(MailMsg != null
    )
    {
    MailMsg(
    this
    , e);
    }
    }
  5. 定义一个方法,将输入转化为期望的事件。
    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);
}

  第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方法,我们应该在其内部注销其登记的所有事件。  

posted on 2011-03-27 21:39  辛勤的代码工  阅读(334)  评论(0编辑  收藏  举报