zoukankan      html  css  js  c++  java
  • 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。。。

  • 相关阅读:
    个人博客
    个人博客
    5.4
    4.30
    4.29
    4.28
    4.27
    4.26
    4.25
    4.24
  • 原文地址:https://www.cnblogs.com/hailiang2013/p/2917240.html
Copyright © 2011-2022 走看看