• 多事件多委托快速管理(ASP.NET开发) yz


    一、何为多事件多委托

          在讲述本节的只是之前,我们先要理解什么是事件、什么是委托。

          委托:如果是从事过C/C++开发的可以理解为一个函数指针。当然你看我的文章一定不是为了这个已经被滥用惯的说法,但是大家一定都知道变量有各种类型,有整数型、浮点型、字符型等等。但是我们经常使用的类型中还有一个是方法(也叫函数),既然我们可以使用,也可以声明,那就应该存在一种可以保存这种变量的类型,在C#中就是委托。稍有不同的是没有一种通用的类型,不同类型的方法(函数)需要我们去定义不同的委托(委托其实就是定义了一种新的类型),并且可以初始化,赋值,甚至可以直接调用这个方法。

        

         事件:就是使用委托这个技术去专门做一方面的事。这个事就是将环境产生的各种事件对应到不同方法,从而方便程序员可以根据当前的事件去管理应用程序。事件其实就是定义一个委托变量,然后定义一个特性去管理这个委托变量。这样的好处是其他人无法调用这个事件,也方面管理。

         通过上面的基础,现在我们就可以讲述我们今天所需要学习的知识,在我们开发的时候不知有没有遇到过一个事件需要多个委托去响应它,甚至可能是多个事件需要多个委托去响应,其中的关系是多对多中间还包含两个事件需要同一个委托去响应。

    而这篇文章将会用很简短的时间去管理这些事件。

    二、思路来源

         本想研究ASP.NET事件的原理,但是后来无意中发现了这个有趣的东西。

    在查看 HtmlButton 类的反编译的代码后发现一个方法的实现步骤:

    当我看到这个之后,我就写了一个 button 标签并且设置 runat='server' 属性,如下:

    其次为了可以看到变化,我也拖了一个 Label 控件在页面中用来反馈结果。

    然后就是在后台编写测试的代码:

    可能会有人很奇怪,为什么 Page_Load 中的代码不放在 IsPostBack 中,这里需要说明下,开始也是放在IsPsotBack中的,

    但是当你点击按钮,页面回传后,对应的事件并没有执行,一开始我认为是这个控件的原因,就是使用了Button服务器控件,但是后来

    的结果还是一样的,经过后来的测试发现其实并没有错,只是在它保存这些事件后,并没有存放在任何位置,自然导致的后果是页面回传

    后这些信息都不会存在了。所以不能放在IsPostBack中。

     解决了点击不响应的问题之后,我们点击按钮看看,结果两个事件都执行了,并且是按照我们赋给ServerClick变量的顺序执行的,执行后的结果:

    这就已经证明了,单个事件可以对应多个委托作为响应,并且响应的顺序是按照我们赋给变量的顺序执行的。唯一的缺点是没有持久化数据。

    三、继续挖掘

       上面的测试证明了我的想法,自然就要继续研究更深的。所以我们继续去查看是什么类型的变量。

    通过一层一层的找,经过它的父类 HtmlContainerControl 类、HtmlControl 类再到Control 类,我们才发现了定义这个变量的类型:

    既然已经找到了变量,我们自然就顺藤摸瓜的去找到它的属性

    发现并没有什么我们想要的,所以我们可以直接看这个EventHandlerList类了:

    其中对于我们比较主要的就是这个 AddHandler 方法,还有 this[] 方法:

    这里我们可以清楚的看到在我们获得这个我们赋给它的委托后,返回的委托仅仅只是 EventHandler ,但是却响应了两个事件委托。原因是 EventHandler 类中其实已经存储了这两个委托

    细心的同志一定发觉到这里使用了单向链表——笔者感觉是模拟了栈

    四、实战使用

       为了能够快速的看到如何使用我们将不通过页面的事件来调用,而是自己手动调用:

    前台:

    1     <form id="form1" runat="server"> 
    2     <div>
    3         <button id="button1"  runat="server">Click Me</button>
    4     </div>
    5         <div>
    6             <asp:Label ID="Label1" runat="server" Text=""></asp:Label>
    7         </div>
    8     </form>

    后台:

     1         private EventHandlerList _events = new EventHandlerList();
     2 
     3         /// <summary>
     4         /// key
     5         /// </summary>
     6         private static readonly object CustonEvent = new object();
     7 
     8         /// <summary>
     9         /// 事件一
    10         /// </summary>
    11         public EventHandler CustomEvent;
    12 
    13         /// <summary>
    14         /// 事件二
    15         /// </summary>
    16         public EventHandler CustomEvent1;
    17 
    18         protected void Page_Load(object sender, EventArgs e)
    19         {
    20             if (!IsPostBack)
    21             {
    22                 CustomEvent = Button1_Click_1;
    23                 CustomEvent1 = Button1_Click_2;
    24                 _events.AddHandler(CustonEvent, CustomEvent);
    25                 _events.AddHandler(CustonEvent, CustomEvent1);
    26                 EventHandler handler = (EventHandler)_events[CustonEvent];
    27                 handler(null, null);
    28             }
    29         }
    30 
    31         protected void Button1_Click_1(object sender, EventArgs e)
    32         {
    33             Label1.Text += "<br /> Click_1 Click";
    34         }
    35 
    36         protected void Button1_Click_2(object sender, EventArgs e)
    37         {
    38             Label1.Text += "<br /> Click_2 Click";
    39         }

    这里我们先给EventHandlerList类型的变量中模拟增加一个事件对应多个委托,

    然后提取出委托并直接运行,会发现最后的结果是两个委托都执行了,并且是按照我们赋值的顺序运行的。

    五、EventHandlerList 的全部代码

      1 [HostProtection(SecurityAction.LinkDemand, SharedState=true)]
      2 public sealed class EventHandlerList : IDisposable
      3 {
      4     // Fields
      5     private ListEntry head;
      6     private Component parent;
      7 
      8     // Methods
      9     [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
     10     public EventHandlerList()
     11     {
     12     }
     13 
     14     [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
     15     internal EventHandlerList(Component parent)
     16     {
     17         this.parent = parent;
     18     }
     19 
     20     public void AddHandler(object key, Delegate value)
     21     {
     22         ListEntry entry = this.Find(key);
     23         if (entry != null)
     24         {
     25             entry.handler = Delegate.Combine(entry.handler, value);
     26         }
     27         else
     28         {
     29             this.head = new ListEntry(key, value, this.head);
     30         }
     31     }
     32 
     33     public void AddHandlers(EventHandlerList listToAddFrom)
     34     {
     35         for (ListEntry entry = listToAddFrom.head; entry != null; entry = entry.next)
     36         {
     37             this.AddHandler(entry.key, entry.handler);
     38         }
     39     }
     40 
     41     public void Dispose()
     42     {
     43         this.head = null;
     44     }
     45 
     46     private ListEntry Find(object key)
     47     {
     48         ListEntry head = this.head;
     49         while (head != null)
     50         {
     51             if (head.key == key)
     52             {
     53                 return head;
     54             }
     55             head = head.next;
     56         }
     57         return head;
     58     }
     59 
     60     public void RemoveHandler(object key, Delegate value)
     61     {
     62         ListEntry entry = this.Find(key);
     63         if (entry != null)
     64         {
     65             entry.handler = Delegate.Remove(entry.handler, value);
     66         }
     67     }
     68 
     69     // Properties
     70     public Delegate this[object key]
     71     {
     72         get
     73         {
     74             ListEntry entry = null;
     75             if ((this.parent == null) || this.parent.CanRaiseEventsInternal)
     76             {
     77                 entry = this.Find(key);
     78             }
     79             if (entry != null)
     80             {
     81                 return entry.handler;
     82             }
     83             return null;
     84         }
     85         set
     86         {
     87             ListEntry entry = this.Find(key);
     88             if (entry != null)
     89             {
     90                 entry.handler = value;
     91             }
     92             else
     93             {
     94                 this.head = new ListEntry(key, value, this.head);
     95             }
     96         }
     97     }
     98 
     99     // Nested Types
    100     private sealed class ListEntry
    101     {
    102         // Fields
    103         internal Delegate handler;
    104         internal object key;
    105         internal EventHandlerList.ListEntry next;
    106 
    107         // Methods
    108         public ListEntry(object key, Delegate handler, EventHandlerList.ListEntry next)
    109         {
    110             this.next = next;
    111             this.key = key;
    112             this.handler = handler;
    113         }
    114     }
    115 }
    116 
    117  
    118 Collapse Methods
    119  

    提醒:如果需要持久化保存请保存至Session中,视图状态无法保存。

  • 相关阅读:
    容器技术(三)搭建本地 Registry【15】
    容器技术(三) 使用公共 Registry【14】
    容器技术(三) 镜像命名的最佳实践【13】
    容器技术(三) RUN vs CMD vs ENTRYPOINT【12】
    容器技术(三) dockerfile常用指令【11】
    容器技术(三) dockerfile调试【10】
    容器技术(三) 镜像缓存特性【9】
    容器技术(三) Dockerfile 构建镜像【8】
    容器技术(三)构建镜像【7】
    layui的图标知识
  • 原文地址:https://www.cnblogs.com/yaozhenfa/p/3090862.html
Copyright © 2020-2023  润新知