当前位置:首页>编程日记>正文

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

http://www.coolblog.cn/news/62bcfdc052f75beb.html

相关文章:

  • asp多表查询并显示_SpringBoot系列(五):SpringBoot整合Mybatis实现多表关联查询
  • s7day2学习记录
  • 【求锤得锤的故事】Redis锁从面试连环炮聊到神仙打架。
  • 矿Spring入门Demo
  • 拼音怎么写_老师:不会写的字用圈代替,看到孩子试卷,网友:人才
  • Linux 实时流量监测(iptraf中文图解)
  • Win10 + Python + GPU版MXNet + VS2015 + RTools + R配置
  • 美颜
  • shell访问php文件夹,Shell获取某目录下所有文件夹的名称
  • 如何优雅的实现 Spring Boot 接口参数加密解密?
  • LeCun亲授的深度学习入门课:从飞行器的发明到卷积神经网络
  • Mac原生Terminal快速登录ssh
  • java受保护的数据与_Javascript类定义语法,私有成员、受保护成员、静态成员等介绍...
  • mysql commit 机制_1024MySQL事物提交机制
  • 支撑微博千亿调用的轻量级RPC框架:Motan
  • jquery 使用小技巧
  • 2019-9
  • 法拉利虚拟学院2010 服务器,法拉利虚拟学院2010
  • vscode pylint 错误_将实际未错误的py库添加到pylint白名单
  • 科学计算工具NumPy(3):ndarray的元素处理
  • 工程师在工作电脑存 64G 不雅文件,被公司开除后索赔 41 万,结果…
  • linux批量创建用户和密码
  • newinsets用法java_Java XYPlot.setInsets方法代碼示例
  • js常用阻止冒泡事件
  • 气泡图在开源监控工具中的应用效果
  • 各类型土地利用图例_划重点!国土空间总体规划——土地利用
  • php 启动服务器监听
  • dubbo简单示例
  • 【设计模式】 模式PK:策略模式VS状态模式
  • [iptables]Redhat 7.2下使用iptables实现NAT
  • Ubuntu13.10:[3]如何开启SSH SERVER服务
  • CSS小技巧——CSS滚动条美化
  • JS实现-页面数据无限加载
  • 阿里巴巴分布式服务框架 Dubbo
  • 最新DOS大全
  • Django View(视图系统)
  • 阿里大鱼.net core 发送短信
  • 程序员入错行怎么办?
  • 两张超级大表join优化
  • 第九天函数
  • Linux软件安装-----apache安装
  • HDU 5988 最小费用流
  • Sorenson Capital:值得投资的 5 种 AI 技术
  • 《看透springmvc源码分析与实践》读书笔记一
  • 正式开课!如何学习相机模型与标定?(单目+双目+鱼眼+深度相机)
  • Arm芯片的新革命在缓缓上演
  • nagios自写插件—check_file
  • python3 错误 Max retries exceeded with url 解决方法
  • 行为模式之Template Method模式
  • 通过Spark进行ALS离线和Stream实时推荐