zoukankan      html  css  js  c++  java
  • 探索.NET中的事件机制

    在我先前一篇博文中已经明确说明事件的本质(返回值)其实是一个委托,既然如此,它必定包含全部委托的特性和意义。我们可以通过简单地定义一个委托,然后把它声明成一个public event就等于公开了一个事件,可以在其它地方通过引用该对象而被订阅触发。通常而言,一个典型的自定义事件可能如下:

    [C#]

    public class MyExample
    {
       public event Action MyEvent;
      
       public void RaiseMyEvent()
       {
           if(MyEvent!=null)
           {
                  MyEvent();
           }
       }
    }
    
    //订阅者
    MyExample mex = new MyExample();
    mex.MyEvent+=(){//你自己的大量事件方法……};

    [VB.NET]

    Public Class MyExample
        Public Event MyEvent As Action
    
        Public Sub RaiseMyEvent()
            RaiseEvent MyEvent()
        End Sub
    End Class
    
    '订阅该事件
    Dim mex As New MyExample()
    AddHandler mex.MyEvent,Sub()
                                             '处理你自己的东西……
                                           End Sub

    这是一个非常基本的雏形处理方法。不过我们可以这样思考——既然微软在类库中为我们提供了大量的委托(除了上面的Action之外,另外一个常见的便是EventHandler吧……或许很多人压根儿没有怎么好好关注它)。既然它们是“通用”委托,那么我们是否可以尝试着把凡是EventHandler(或者Action的)事件存在一起,同时又根据它们不同的时间类型进行“分门别类”归档呢(你自己想想看,如果你的类中具备多个EventHandler或是Action类型的事件,反复定义不感到累么?)本章节就将揭秘微软是如何处理此类情况的。

    我们先使用Telerik的Just-DeCompiler工具反射查看一个普通的WinForm中Button控件的Click事件:

    Button控件继承自ButtonBase(这个类是一个抽象类,用于描述一般“按钮类”控件的大致情形,因此如果要自定义某个按钮可继承此类,这里不展开讨论)。而ButtonBase又继承自Control类,Control类中包含了Click事件,反射后的代码如下:

    [C#]

    [SRCategory("CatAction")]
    [SRDescription("ControlOnClickDescr")]
    public event EventHandler Click
    {
        add
        {
            base.Events.AddHandler(Control.EventClick, value);
        }
        remove
        {
            base.Events.RemoveHandler(Control.EventClick, value);
        }
    }

    [VB.NET]

    <SRCategory("CatAction")> _
    <SRDescription("ControlOnClickDescr")> _
    Public Custom Event Click As EventHandler
        AddHandler
            MyBase.Events.AddHandler(Control.EventClick, value)
        End AddHandler
        RemoveHandler
            MyBase.Events.RemoveHandler(Control.EventClick, value)
        End RemoveHandler
    End Event

    可见这个事件并不是像我们原先一般简单定义一个event就可以了。这个定义貌似有些像“属性”——可以任意增加或者删除事件!的确如此!我们不要忘记了委托是可以+=或者-=的(因为任意一个委托都是继承自Delegate,而这个类具备了Combine和Remove方法,+=或者-=是一个语法糖而已,会自动被CLR转化成Combine或者是Remove)。事件也如此——一个默认的定义会被CLR自动转换成Add/Remove的方式。

    继续跟踪发现base.Events的Events是一个EventHandlerList(.NET中XXXList肯定是一个列表集合),估计对一系列EventHandler 事件的实例全部添加/删除操作。

    [C#]

    [HostProtection(SecurityAction.LinkDemand, SharedState=true)]
    public sealed class EventHandlerList : IDisposable
    {
        private EventHandlerList.ListEntry head;
    
        private Component parent;
    
        public Delegate this[object key]
        {
            get
            {
                EventHandlerList.ListEntry listEntry = null;
                if (this.parent == null || this.parent.CanRaiseEventsInternal)
                {
                    listEntry = this.Find(key);
                }
                if (listEntry == null)
                {
                    return null;
                }
                else
                {
                    return listEntry.handler;
                }
            }
            set
            {
                EventHandlerList.ListEntry listEntry = this.Find(key);
                if (listEntry == null)
                {
                    this.head = new EventHandlerList.ListEntry(key, value, this.head);
                    return;
                }
                else
                {
                    listEntry.handler = value;
                    return;
                }
            }
        }
    
        public EventHandlerList()
        {
        }
    
        internal EventHandlerList(Component parent)
        {
            this.parent = parent;
        }
    
        public void AddHandler(object key, Delegate value)
        {
            EventHandlerList.ListEntry listEntry = this.Find(key);
            if (listEntry == null)
            {
                this.head = new EventHandlerList.ListEntry(key, value, this.head);
                return;
            }
            else
            {
                listEntry.handler = Delegate.Combine(listEntry.handler, value);
                return;
            }
        }
    
        public void AddHandlers(EventHandlerList listToAddFrom)
        {
            for (EventHandlerList.ListEntry i = listToAddFrom.head; i != null; i = i.next)
            {
                this.AddHandler(i.key, i.handler);
            }
        }
    
        public void Dispose()
        {
            this.head = null;
        }
    
        private EventHandlerList.ListEntry Find(object key)
        {
            EventHandlerList.ListEntry listEntry = this.head;
            while (listEntry != null && listEntry.key != key)
            {
                listEntry = listEntry.next;
            }
            return listEntry;
        }
    
        public void RemoveHandler(object key, Delegate value)
        {
            EventHandlerList.ListEntry listEntry = this.Find(key);
            if (listEntry != null)
            {
                listEntry.handler = Delegate.Remove(listEntry.handler, value);
            }
        }
    
        private sealed class ListEntry
        {
            internal EventHandlerList.ListEntry next;
    
            internal object key;
    
            internal Delegate handler;
    
            public ListEntry(object key, Delegate handler, EventHandlerList.ListEntry next)
            {
                this.next = next;
                this.key = key;
                this.handler = handler;
            }
        }
    }

    [VB.NET]

    <HostProtection(SecurityAction.LinkDemand, SharedState:=True)> _
    Public NotInheritable Class EventHandlerList
        Implements IDisposable
        Private head As EventHandlerList.ListEntry
    
        Private parent As Component
    
        Default Public Property Item As Delegate(ByVal key As Object)
            Get
                Dim listEntry As EventHandlerList.ListEntry = Nothing
                If (Me.parent = Nothing OrElse Me.parent.CanRaiseEventsInternal) Then
                    listEntry = Me.Find(key)
                End If
                If (listEntry = Nothing) Then
                    Return Nothing
                Else
                    Return listEntry.handler
                End If
            End Get
            Set(ByVal value As Delegate)
                Dim listEntry As EventHandlerList.ListEntry = Me.Find(key)
                If (listEntry = Nothing) Then
                    Me.head = New EventHandlerList.ListEntry(key, value, Me.head)
                    Return
                Else
                    listEntry.handler = value
                    Return
                End If
            End Set
        End Property
    
        Public Sub New()
            MyBase.New()
        End Sub
    
        Friend Sub New(ByVal parent As Component)
            MyBase.New()
            Me.parent = parent
        End Sub
    
        Public Sub [AddHandler](ByVal key As Object, ByVal value As Delegate)
            Dim listEntry As EventHandlerList.ListEntry = Me.Find(key)
            If (listEntry = Nothing) Then
                Me.head = New EventHandlerList.ListEntry(key, value, Me.head)
                Return
            Else
                listEntry.handler = Delegate.Combine(listEntry.handler, value)
                Return
            End If
        End Sub
    
        Public Sub AddHandlers(ByVal listToAddFrom As EventHandlerList)
            Dim i As EventHandlerList.ListEntry = listToAddFrom.head
            Do
                Me.AddHandler(i.key, i.handler)
                i = i.next
            Loop While i <> Nothing
        End Sub
    
        Public Sub Dispose()
            Me.head = Nothing
        End Sub
    
        Private Function Find(ByVal key As Object) As EventHandlerList.ListEntry 
            Dim listEntry As EventHandlerList.ListEntry = Me.head
            While listEntry <> Nothing AndAlso listEntry.key <> key
                listEntry = listEntry.next
            End While
            Return listEntry
        End Function
    
        Public Sub RemoveHandler(ByVal key As Object, ByVal value As Delegate)
            Dim listEntry As EventHandlerList.ListEntry = Me.Find(key)
            If (listEntry <> Nothing) Then
                listEntry.handler = Delegate.Remove(listEntry.handler, value)
            End If
        End Sub
    
        Private NotInheritable Class ListEntry
            Friend next As EventHandlerList.ListEntry
    
            Friend key As Object
    
            Friend handler As Delegate
    
            Public Sub New(ByVal key As Object, ByVal handler As Delegate, ByVal next As EventHandlerList.ListEntry)
                MyBase.New()
                Me.next = next
                Me.key = key
                Me.handler = handler
            End Sub
        End Class
    End Class

    果然不错,纵观整个EventHandlerList类是一个单链表类。其中每一个节点包含“Key”(用于判断究竟何种事件类型),"Handler"(某个事件的委托,之所以用Delegate是为了通用性强——任意的委托实体都可以传入其中),next指向下一个节点的引用(指针)。

    现在让我们回过头来看看这样设计运行流程:

    1)当我们对Button的Click事件进行+=的时候,也就是对Control类的Click进行了事件添加,那么也就是对Events实体进行操作(传入一个“事件类型”和一个事件委托具体指向的函数实体value)。

    2)由于Events实体就是EventHandlerList对象,因而调用其方法Addhandler,首先判断Key是否存在,如果存在,那么直接对已有的委托实体进行Combine操作(多路委托,这就可以解释为什么一个事件绑定多个方法之后会按照顺序依次执行)。如果不存在,那么则生成一个新的节点,存放这个委托实体。

  • 相关阅读:
    自定义滚动条原理
    多个轮播图或者选项卡显示在一个页面是,使用代码重用
    浮动与清除
    cni 添加网络 流程分析
    《MapReduce: Simplified Data Processing on Large Cluster 》翻译
    OpenStack overview 笔记
    docker containerd shim分析
    docker containerd 中的create 容器操作
    MIT jos 6.828 Fall 2014 训练记录(lab 6)
    docker containerd中的容器操作
  • 原文地址:https://www.cnblogs.com/ServiceboyNew/p/2703617.html
Copyright © 2011-2022 走看看