Chapter11 事件

春节过去了,继续去年的学习。。。。
轮到事件了,这个东西从我一开始接触.net时就常常听到别人提起,以前不知道怎么回事,现在感觉C#事件是.net中非常重要的一块,像asp.net ,winform等我们常拖的控件,都是基于事件驱动处理的封装好的框架。

什么是事件

宏观的理解就是,如果类型定义了事件成员,那么类型或类型的实例就可以通知其他对象发生了特定的事情。

记得上学时老师教过一种设计模式——观察者模式,跟事件很像,一个观察者发现老板来了,然后通知所有跟他打过招呼的员工老板来了,要干活啦。

在CLR中事件模型是建立在委托的基础上的。委托这个东西很牛逼,在下一节接着讲。。。

说起来似乎都可以听得懂,还是举一个常用的例子来实际操作一下比较好,场景:一封新邮件到达,希望也同时转发给传真机或寻呼机。

这样的话需要有个邮件转发中心来转发新邮件,命名为MailManager。

还的有2个对象传真机and寻呼机。命名为Fax和Pager

既然是邮件那肯定得有发件人,收件人,邮件标题等等。咱们玩的是事件,C#中有个EventArgs类来专门容纳所有需要发送事件通知者的附加信息。继承它重新派生一个新的类命名时以EventArgs结束。。

下面是整个场景的构建思路,如图:

场景

这样的话思路就比较清晰了,接下来就是如何玩事件并学好掌握它。

1.第一步先来把这个邮件信息(即容纳所有需要发送给事件通知者的附加信息)对象给完成。注意派生自EventArgs命名时必须以EventArgs结尾,书上这么说的我也不知道为什么,西西。

    internal class NewMailEventArgs:EventArgs
    {
        private readonly String m_from, m_to, m_subject;

        public NewMailEventArgs(String m_from,String m_to,String m_subject)
        {
            this.m_from = m_from;
            this.m_to = m_to;
            this.m_subject = m_subject;
        }

        public String m_From
        {
            get { return m_from; }
        }
        public String m_To
        {
            get { return m_to; }
        }
        public String m_Subject
        {
            get { return m_subject; }
        }
    }

2.好接下来第二步就是邮件中转,在这里定义了事件以及事件的触发。

以下简单的几行代码需要注意的地方:

  1. 很显然,这个事件在大多数情况下是公开的,这样其他的代码才能访问该事件成员
  2. 还有一个很重要的类型EventHandler<NewMailEventArgs> ,首先他是一个委托类型,这就意味这所有事件通知的接受者都必须提供一个与EventHandler<NewMailEventArgs>委托类型匹配的回调方法,这里EventHandler是泛型委托类型其定义如下
    public delegate void EventHandler<TEventArgs>(object sender,TEventArgs e) where TEventArgs:EventArgs

    所以方法原型必须是viod Method(object sender,NewMailEventArgs e);

  3. 在定义引发事件的方法时出于线程安全的考虑将委托字段NewMail的引用复制到临时字段Temp中,temp(this,e)如果谁订阅了事件,则就会执行谁的回调方法。
  4. OnNewMail方法有一关键字virtual目的是为了使派生类可以重写该方法,这个能力使派生类能控制事件的引发,从而以自己的方式处理电子邮件。
    internal class MailManager
    {
        //事件的定义
        public event EventHandler<NewMailEventArgs> NewMail;

        //引发事件的方法
        protected virtual void OnNewMail(NewMailEventArgs e)
        {
            EventHandler<NewMailEventArgs> temp = Interlocked.CompareExchange(ref NewMail, null, null);

            if (temp!=null)
            {
                temp(this, e);
            }
        }

        public void SimulateNewMail(String from, String to, String subject)
        {
            NewMailEventArgs e = new NewMailEventArgs(from, to, subject);

            OnNewMail(e);
        }
    }

接下来定义一个方法来接受邮件信息的来转化为事件

    internal class MailManager
    {
        
        //其他成员...

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

在设计侦听事件的类型,Fax和Pager对象之前还是有必要了解下编译器是如何实现事件的,就是说你定义一个事件很简单,但是它是如何工作的???

定义一个事件只需一行代码,C#编译器会把它转换为3个构造

  1. 初始化事件为null,即
    private event EventHandler<NewMailEventArgs> NewMail=null; 这里始终是private
    
  2. 一个公共add_Xxx方法(其中Xxx是事件名),它允许其他对象登记对事件的关注,它的可访问性与定义事件时的访问性一致,如果是pulbic 则public void add_Xxx(EventHandler<TEventArgs> value)
  3. 一个公共remove_Xxx方法(Xxx是事件名),它允许一个对象注销对事件的关注,同上。。。。

3.设计侦听事件的类型,Fax和Pager对象

mm.NewMail+=FaxMsg;

等价于

mm.add_NewMail(new EventHandler<NewMailEventArgs>(this.FaxMsg));

    internal class Fax
    {
        public Fax(MailManager mm)
        {
            mm.NewMail += this.FaxMsg;
        }

        private void FaxMsg(Object sender, NewMailEventArgs e)
        {
            Console.WriteLine("Fax mail message:");
            Console.WriteLine("From={0},To={1},Subject={2}",e.m_From,e.m_To,e.m_Subject);
        }
    }

4.Main函数入口

    class Program
    {
        static void Main(string[] args)
        {
            MailManager mm = new MailManager();
            Fax f = new Fax(mm);
            Pager p = new Pager(mm);

            mm.SimulateNewMail("me", "all", "hello world");

        }
    }

接下来还有显示实现事件

就是说开发人员可以控制add和remove方法操纵回调委托的方式,可以提高执行效率。

例如某个类型含有大量的事件,为了高效率存储事件委托,公开了事件的每个对象都要维护一个集合(通常是一个字典),这个集合将某种形式的事件标识符作为Key,将一个委托列表作为Value。。。

posted @ 2013-02-19 16:04  hailiang2013  阅读(167)  评论(0编辑  收藏  举报