• 说说.net事件和委托


    原文: http://www.cnblogs.com/lovebaobao/archive/2008/12/17/1356769.html
       一说到.net的事件,也许你会想都说教程满天飞,一个被说烂了的东西还有什么可以说的啊?是啊,的确有很多好文章剖析事件,比如张子阳先生的C# 中的委托和事件
    重温Observer模式--热水器·改 这两篇文章让我弄懂了委托、事件和观察者模式的基础知识,另外深入的事件文章还有博客堂 破宝的事件三部曲,(btw 这些都是我看过的,如果你见见过更好的文章请跟帖以便更多人学习,谢谢。:))
       现在来说下这个被说烂了的东东我感觉需要注意的地方。
    1 单播和多播事件
    2 事件的显式定义(继而解释委托和事件的区别)
    3 .net事件模型

      对于单播和多播事件概念。查资料是这么定义的:单播事件就是对象(类)发出的事件通知,只能被外界的某一个事件处理程序处理,而不能被多个事件处理程序处理,多播事件是对象(类)发出的事件通知,可以同时被外界不同的事件处理程序处理。

       说是这么简单,理解清楚不是很简单。有没有想过这里是到底怎么实现的呢?这里我读过《.net框架程序设计》第17.5 委托史话:System.Delegate与system.MulticastDelegate,如果有兴趣可以找来看看,system.MulticastDelegate定义于FCL继承自System.Delegate,这里MulticastDelegate其实就是多播委托,那么多播事件也是通过这个实现的,不用说Delegate大家都可以猜到是单播委托了,那么平时我们定义一个委托public delegate void Back(Object value, Int32 item, Int32 numItems)

    当编译器遇到这句委托定义,会产生一个完整的类定义:

    public class Back : System.MulticastDelegate
    {
        public Back(Object target, Int32 methodPtr);

        public void virtual Invoke(Object value, Int32 item, Int32 numItems);
     
        public virtual IAsyncResult BeginInvoke(Object vlaue ,Int32. numItems, AsyncCallback callback,Object object);
     
      public virtual void EndInvoke(IAsyncResult result);
    }
        这个类其内部的方法看不懂没关系,先看这个类的Back是继承自System.MulticastDelegate,也就是说我们平时定义的委托几乎都是继承自多播委托的,那么为什么要有单播委托,这个具《.net框架程序设计》上说是微软.net框架设计的一个缺陷。所以这里大家记住平时定义的委托基本上都是多播的,也就是都可以用+=操作把委托组合成链,这里我不能不说破宝对多播和单播的理解有误。
      事件的显式定义,也许你还不知道显式定义是怎么回事,相信很多朋友平时自己定义事件也没注意过这个问题。

    回忆下平时我们是怎么定义事件的呢?是不是下面的样子:

    class MailManager
    {

      
    //定义一个委托类
      public delegate void MailMesgEventHandler(Object sender, EventArgs e);
      
     
    //定义对应委托的事件
      public event MailMesgEventHandler MailMsg;
    }
       我们需要为事件先定义一个委托类(这里EventArgs我省略没自己定义特定子类),然后用这个委托类型定义事件。
    看了很简单,是的,这里就是隐式定义事件,为什么叫隐式呢,我自己弄的名字哈哈,编译这句事件定义代码时要产更多的代码,就像下面这些简化的伪码:

    private MailMesgEventHandler MailMesg = null;

     public void add_MailMesg(MailMesgEventHandler handler)
     {
      MailMesg = (MailMesgEventHandler);
      Delegate.Combine(MailMsg, handler);
     }

     public void remove_MailMesg(MailMesgEventHandler handler)
     {
      MailMesg = (MailMesgEventHandler);
      Delegate.Remove(MailMsg, handler);
     }
       可以看到这里编译器产生了三块东西,第一块是定义一个委托字段,下面是对这个字段的add和remove访问器,说到字段访问器,你也许会说属性!哈哈,是的,事件其实就是委托字段的访问器,和属性非常像,你可以这么理解,事件就是委托的属性,前面这句是从网上看到的,只不过属性用的是get和set,事件是add和remove
    待会我们谈显式定义事件的时候,你会更确定事件就是委托的属性!
     看来是时候说显式定义事件啦,先看看下面的代码:

    public delegate void MailMesgEventHandler(Object sender, EventArgs e);
    private MailMesgEventHandler MailMesg;
    public event MailMesgeventHandler MailMsg
    {
     add
     {
      MailMesg = (MailMesgEventHandler);
      Delegate.Combine(MailMsg, handler);
     }
     
     remove
     {
      MailMesg = (MailMesgEventHandler);
      Delegate.Remove(MailMsg, handler);
     }
    }
       现在很像属性了吧,和隐式生成代码比较,这里不同的是add和remover的写法而已,那么显式定义的好处就很明显了
    我们可以控制add/remove内部的逻辑,这样可以在+=(其实就是add,这里是操作符重载)和-=(其实就是remove)时更灵活。
      最后一个概念是.net事件模型,说事件模型之前先考虑一个问题,我们不管是隐式还是显式定义事件最后都要定义一个委托字段,大家知道System.Windows.Forms.Control类型中大约60个事件,如果Control类型在实现这些事件的时候让编译器自动产生委托字段以及add和remove访问器方法,那么每个Control类型将仅仅因为事件就有将近60个委托,
    由于我们大多数时候在对象上登记的事件都很少,因此没创建一个Control类型(以及继承自Control的类型)的实例都会很浪费内存,顺便说下System.Web.UI.Control类型也存在这样的问题。
    (以上斜体字是载自《.net框架程序设计修订版》238-239页)
    那么我们怎么做能可以既定义事件又同时省去这些委托字段呢?

    先看个图:



       看完了图你是不是感觉如果用个表来存储委托key/值,如果添加委托,首先查询其中有没有相应的关键字,没有这时添加进去,有就合并到委托链。
    这是个美妙的想法,同时可以各种委托有一个相同的“老窝”,而每个委托的工作又不相互干扰。
    其实微软就给我们提供了这样的家,它就是System.ComponetModel.EventHandlerList
    那么至于里面怎么实现的我是不知道,不过现在我们可以自己动手做个委托“老窝”。(注下面代码全部载自《.net框架程序设计修订版,》)

    Code
       以上是一个利用Hashtable存储委托的实现类,下面我自己写了个委托使用的具体案例,如下。
    Code
      需要说明的是,EventHandlerSet只有在多处使用时才能体现出来其优势,因为为每个事件定义关键字也需要内存,这里我定义成静态类的字段是有目的的,有兴趣可以想想原因。

    好了到这里基本说完了,你看完了这篇事件的东东会不会再次说——都说破啦,哈哈。

  • 相关阅读:
    IE6,7,8在boostrap中兼容h5和css3
    Bootstrap表格类名及对应图形
    兼容低于IE9不支持html5标签的元素的方法
    transition与animation的区别
    position与offset的区别
    天坑之mysql乱码问题以及mysql重启出现1067的错误解决
    如何远程连接Windows server上的MySQL服务
    mysql如何让自增id从1开始设置方法
    mybatis association嵌套association的两级嵌套问题
    @Param注解在dao层的使用
  • 原文地址:https://www.cnblogs.com/answercard/p/1383536.html
Copyright © 2020-2023  润新知