zoukankan      html  css  js  c++  java
  • 用 C# 做组件设计时的事件实现方法讨论

    事件,其实就是将物体的某个过程处理通过委托(delegate, 也就是函数指针) 的方式公开给外部的自定义函数处理。 C# 可以使用多播委托,但实际上一般情况下只需要用到单播。

    事件需要通过调用到那个委托的代码触发才可以被调用。
    以下用例子来说明。首先我们定义一个委托:

    namespace EventDemo
    {
        public delegate void ProcessHandler(object sender);
    }
    

    最简单的事件定义方式:
    namespace EventDemo
    {
        public class Class1
        {
            private event ProcessHandler _processHandler = null;
            public event ProcessHandler ProcessStart
            {
                add { _processHandler += value; }
                remove { _processHandler -= value; }
            }
            /// 
            /// 触发事件的某个方法
            /// 
            public void Process()
            {
                _processHandler(this);
                for (int i = 0; i < 10; i++)
                    i = i + 1;
            }
            public Class1()
            {
            }
        }
    }
    

    Class1 使用原始的事件定义方式, 有一个问题,如果事件非常多的时候,每一个事件都要对应一个相应的私有的委托成员(函数指针)。在窗口程序里尤其可怕,因为 Windows 窗口消息数以千计。这样会造成很庞大的内存消耗。
    这个模式需要改进为 Class2。代码如下:

    namespace EventDemo
    {
        using System.Collections;
        public class Class2
        {
            private Hashtable _eventList = new Hashtable();
            // 每一种事件会对应一个相应的静态变量作为他们在 Hashtable 中的 keys.
            private static object _processStart = new object();
            private static object _processEnd = new object();
            public event ProcessHandler ProcessStart
            {
                add { _eventList.Add(_processStart, value); }
                remove { _eventList.Remove(_processStart); }
            }
            public event ProcessHandler ProcessEnd
            {
                add { _eventList.Add(_processEnd, value); }
                remove { _eventList.Remove(_processEnd); }
            }
            public void Process()
            {
                ProcessHandler start = (ProcessHandler) _eventList[_processStart];
                ProcessHandler end = (ProcessHandler) _eventList[_processEnd];
                if (start != null)
                    start(this);
                for (int i = 0; i < 10; i++)
                    i = i + 1;
                if (end != null)
                    end(this);
            }
            public Class2()
            {
            }
        }
    }
    

    Class2 中,每一种事件定义一个相应的静态变量作为他们在 Hashtable 中的 keys.
    Hashtable 作为函数指针的容器,是私有的。
    这样实际上是 Lazy Allocate 模式,大大减小了内存的开销。
    但该实现也有问题,因为每个 key 只对应一个 value,所以不能支持 multicast 的事件。

    在 .net 中,通常继承自 Component 类来实现这种基础架构。代码如下:

    namespace EventDemo
    {
        using System;
        using System.ComponentModel;
        public class Class3 : Component
        {
            private static object _processStart = new object();
            public event EventHandler ProcessStart
            {
                add { Events.AddHandler(_processStart, value); }
                remove { Events.RemoveHandler(_processStart, value); }
            }
            public void Process()
            {
                EventHandler handler = (EventHandler) Events[_processStart];
                if (handler != null)
                    handler(this, null);
            }
            public Class3()
            {
            }
        }
    }
    

    Component 类的实现是完整的,支持 Multicast 委托。我们用 Reflector 看一下该类的代码,会看到有一个叫做 EventHandlerList 的类,代码如下:

    public sealed class EventHandlerList : IDisposable
    {
        // Methods
        public EventHandlerList()
        {
        }
     
        public void AddHandler(object key, Delegate value)
        {
            EventHandlerList.ListEntry entry1 = this.Find(key);
            if (entry1 != null)
            {
                entry1.handler = Delegate.Combine(entry1.handler, value);
            }
            else
            {
                this.head = new EventHandlerList.ListEntry(key, value, this.head);
            }
        }
     
        public void Dispose()
        {
            this.head = null;
        }
         
        private EventHandlerList.ListEntry Find(object key)
        {
            EventHandlerList.ListEntry entry1 = this.head;
            while (entry1 != null)
            {
                if (entry1.key == key)
                {
                    break;
                }
                entry1 = entry1.next;
            }
            return entry1;
        }
     
        public void RemoveHandler(object key, Delegate value)
        {
            EventHandlerList.ListEntry entry1 = this.Find(key);
            if (entry1 != null)
            {
                entry1.handler = Delegate.Remove(entry1.handler, value);
            }
        }
        // Properties
        public Delegate this[object key]
        {
            get
            {
                EventHandlerList.ListEntry entry1 = this.Find(key);
                if (entry1 != null)
                {
                    return entry1.handler;
                }
                return null;
            }
            set
            {
                EventHandlerList.ListEntry entry1 = this.Find(key);
                if (entry1 != null)
                {
                    entry1.handler = value;
                }
                else
                {
                    this.head = new EventHandlerList.ListEntry(key, value, this.head);
                }
            }
        }
        // Fields
        private ListEntry head;
        // Nested Types
        private sealed class ListEntry
        {
            // Methods
            public ListEntry(object key, Delegate handler, EventHandlerList.ListEntry next)
            {
                this.next = next;
                this.key = key;
                this.handler = handler;
            }
            // Fields
            internal Delegate handler;
            internal object key;
            internal EventHandlerList.ListEntry next;
        }
    }
    

    这个类实现了一个事件的链表数据结构。其中每一个具体的事件是采用 Delegate.Combine(), Delegate.Remove() 方法来添加和删除具体的 delegate. 所以这个的实现和 Class2 的实现相比功能更加完整了。

    Component 类的代码是这样的:

    [DesignerCategory("Component")]
    public class Component : MarshalByRefObject, IComponent, IDisposable
    {
        private EventHandlerList events;
        protected EventHandlerList Events
        {
            get
            {
                if (this.events == null)
                {
                    this.events = new EventHandlerList();
                }
                return this.events;
            }
        }
        // ...
    }
    

    它简单的通过提供 Events 这个属性,让设计者可以自由的实现各种属性。

    (注:本文的 Class1 ~ Class3 代码范例来自黄忠成的《深入剖析 asp.net 组件设计》一书。)
  • 相关阅读:
    GPU CUDA之——深入理解threadIdx
    需求分析、业务逻辑与数据结构
    软件建模的本质
    浅谈软件需求建模
    软件建模即程序设计
    软件开发从0到1与软件建模
    数据模型所描述的内容包括三个部分:数据结构、数据操作、数据约束。
    观察力与信息搜集能力
    人类为什么写书
    鲁宾斯坦说:"思维是在概括中完成的。"
  • 原文地址:https://www.cnblogs.com/RChen/p/265575.html
Copyright © 2011-2022 走看看