C#事件

如果定义一个事件,就意味着类型要提供以下能力

1.方法可登记它对该事件的关注

2.方法可注销它对该事件的关注

3.该事件发生时,登记了的方法会收到通知

类型之所以能提供事件通知的功能,是因为类型维护了一个已登记方法的列表,事件发生后,类型将通知列表中所有已登记的方法。

CLR的事件是建立在委托的基础上,委托是调用回调方法的一种类型安全的方式,对象通过回调方法来接受它们订阅的通知。

场景:电子邮件应用程序,当电子邮件到达时,用户希望将该邮件转发给传真机或寻呼机

clipboard

1.1、第一步:定义类型来容纳所有需要发送给事件通知接受者的附加信息

事件引发时,引发事件的对象可能希望向接收事件通知的对象传递一些附加信息。好吧,我们这个类可以这样定义

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; } }

    }

EventArgs类:

clipboard[1]

1.2、第二步:定义事件成员

事件成员使用C#关键字event来定义,每个事件成员都要指定以下内容:

1.一个可访问性标识符(几乎肯定是public,因为要暴漏给其他订阅者)

2.一个委托类型,它要指出要调用的方法的原型

internal class MailManager{

    //第二步定义事件成员
    public event EventHandler<NewMailEventArgs> NewMail;

}

clipboard[2]

.3、第三步:定义负责引发事件的方法来通知事件的登记对象

类应定义一个受保护的虚方法。要引发事件时,当前类及其派生类中的代码会调用该方法。该方法要获取一个参数,也就是一个NewMailEventArgs对象(包括附加信息),该方法默认实现只检查是否有对象登记了对事件的关注,如果有就引发事件,通知登记者

internal class MailManager

    {

        public event EventHandler<NewMailEventArgs> NewMail;

        protected virtual void OnNewMail(NewMailEventArgs e)
        {
            //出于线程安全考虑,现将对委托字段引用复制到一个临时字段中
            EventHandler<EventArgs> temp = System.Threading.Interlocked.CompareExchange(ref NewMail, null, null);

            //任何方法登记了对事件的关注,就通知他们
            if (temp!=null)
            {
                temp(this, e);
            }
        }
    }

clipboard[3]

为了方便起见,可以定义一个扩展方法来封装这个线程安全逻辑

public static class EventArgExtensions{
    public static void Raise<TEventArgs>(this TEventArgs e,Object sender,ref EventHandler<TEventArgs> eventDelegate)
    where TEventArgs:EventArgs{
        //出于线程安全考虑,现将对委托字段引用复制到一个临时字段中
        EventHandler<EventArgs> temp = System.Threading.Interlocked.CompareExchange(ref NewMail, null, null);
        //任何方法登记了对事件的关注,就通知他们
      if (temp!=null)
        {
            temp(this, e);
        }
    }
}

现在,可以像下面这样重写OnNewMail方法:

protected virtual void OnNewMail(NewMailEventArgs e){
    e.Raise(this,ref m_NewMail);
}

 

1.4、第四步:定义方法将输入转化为期望事件

现在还需要一些方法来获取输入,并把它转化为事件的引发,在MailManager的例子中是调用SimulateNewMail方法来指出一封新的电子邮件已到达MailManager:

internal class MailManager{

    //第四步:定义方法将输入转化为期望事件

    public void SimulateNewManager(string from ,string to,string subject){
        //构造一个对象来容纳想传给通知接受者的信息
        NewMailEventArgs e=new NewMailEventArgs(from,to,subject);
        //调用虚方法通知对象事件已发生
        //如果没有类型重写该方法,我们的对象将通知事件的所有登记对象
        OnNewMail(e);
    }
}

clipboard[4]

1.5、第五步:设计订阅者,及回调方法

internal sealed class Fax{
    //MailManager传给构造器
   public Fax(MailManager mm)
    {
        //构造EventHandler<NewMailEventArgs>委托的一个实例
        //使他引用我们的FaxMsg回调方法
        //向MailManager的NewMail事件登记我们的回调方法
        mm.NewMail+= FaxMsg;
    }
    //回调方法,新电子邮件到达,MailManager将回调此方法
    //'sender'表示MailManager对象,便于将信息回传给它
    //'e'表示MailManager对象想传递给我们的附加事件信息
    private void FaxMsg(object sender,NewMailEventArgs e)
    {
         Console.WriteLine("Faxing mail message:");
     Console.WriteLine("From={0}, To={1}, Subject={2}",e.From,e.To,e.Subject);
    }
    //Fax对象将向NewMail事件注销自己对它的关注,以后便不再接受通知
    public void Unregister(MailManager mm)
    {
        //向MailManager的NewMail事件注销自己对这个事件的关注
        mm.NewMail-=FaxMsg;
    }
}

 

1.6、程序入口:

class Program
    {
        static void Main(string[] args)
        {
            //初始化邮件管理
            MailManager mm = new MailManager();
            //Fax订阅邮件
            Fax f = new Fax(mm);
            //Pager订阅邮件
            Pager p = new Pager(mm);
            //新邮件到达,通知订阅者,调用它们的回调函数
            mm.SimulateNewManager("boss", "all", "meetting");
        }
    }

clipboard[5]

posted @ 2012-11-20 11:31  Lordbaby  阅读(204)  评论(0编辑  收藏  举报