用 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 组件设计》一书。)