/*
*
* 自定义事件类型
*
*
*
* 摘抄于:<Microsoft.net框架设计>
* email:suiqirui1987@163.com
*
* QQ:492732033
*
* copyright:@@@@ suiqirui19872005.cnblogs.com
*
*
*
* 思想:
*
* 让每个对象都保存一个事件/委托对的集合(通常为一个散列表).当一个新的对象
*
* 被构造时.该集合是空的.当有事件被登记时.我们将在集合中查找该事件
*
* .如果集合中存在该事件.那新的委托实例将衩组全到表示该事件的委托链表上.否则,该事件和新的委托实例将直接被
*
* 添加到集合中..
*
*
* 当事件需要触发时,我们还是首先在集合中查找该事件.如果集合不存在该事件.也就是说
*
* 该事件没有被登记.那么将没有任何委托实例被调用..如果集合中存在该事件.
*
* 那么其对应的委托链表将被调用
*
*
*
*/
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections;
using System.Runtime.CompilerServices;
#region EventHandlerSet类
/*
* --------> EventHandlerSet类 <-----------
*
* 注意:
* FCL中定义有一个名为System.ComponentModel.EventHandlerList的类型。该类型和上面展示的EventHandlerSet
*
* 类型所做的事情基本上是一样的。System.Window.Forms.Control和System.Web.UI.Control 类型在维护它们各自的稀疏事件集合时
*
* 使用的就是EventHandlerList这个类型。。 大家当然可以选择使用FCl中EventHandlerList 类型。该类型和我们上面展示的EventHandlerSet
*
* 类型的不同之处在于EventHandlerList类型在内部使用的是一个链表,而不是散列表
*
* 这意味着访问EventHandlerList类型中的元素要比访问EventHandlerSet类型中的元素慢一些.
*
* 另外.EventHandlerList类型没有提供任何线程安全的访问方式.因此在某些情况下.
*
* 大家还需要自已实现一个对EventHandlerList类型的线程安全的封装
*
*
*
*
*/
public class EventHandlerSet : IDisposable
{
//用于保存"事件键/委托值"对的私有散列表
private Hashtable events = new Hashtable();
//一个索引器,用获取或设置与传入的事件对象的
//散列键相关联的委托
public virtual Delegate this[Object eventKey] {
//如果对象不在集合中,则返回null
get {
return (Delegate)events[eventKey];
}
set {
events[eventKey] = value;
}
}
//指定的事件对象的散列刍对应的委托链表上添加/组全一个委托实例
public virtual void AddHandler(object eventKey, Delegate handler) {
events[eventKey] = Delegate.Combine((Delegate)events[eventKey], handler);
}
public virtual void RemoveHandler(Object eventKey, Delegate handler) {
events[eventKey] = Delegate.Remove((Delegate)events[eventKey], handler);
}
//在指定的事件对象散列键盘对应的委托链表上触发事件
public virtual void Fire(Object eventKey, Object sender, EventArgs e) {
Delegate d = (Delegate)events[eventKey];
if (d != null) {
d.DynamicInvoke(new object[] { sender, e });
}
}
//释放对象以使散列表占用的内存资源在下一次垃圾收集中被回收,从而阻止垃圾收集器提升其性能
public void Dispose()
{
events = null;
}
//下而的静态方法返回一个对传入的EventHandlerSet对象的线程安全的封装
public static EventHandlerSet Syschronized(EventHandlerSet eventHandlerSet)
{
if (eventHandlerSet == null)
{
throw new ArgumentNullException("eventHanlderSet");
}
return new SysnchronizedEventHandlerSet(eventHandlerSet);
}
//下面类在EventHandlerSet类的基础上提供了一个线程安全的封装
private class SysnchronizedEventHandlerSet : EventHandlerSet{
//引用非线程安全的对象
private EventHandlerSet eventHandlerSet;
//在非线程安全的对象上构造一个线程安全的封装
public SysnchronizedEventHandlerSet(EventHandlerSet eventHandlerSet){
this.eventHandlerSet=eventHandlerSet;
Dispose();//释放基类中的散列表对象
}
//线程安全的索引器
public override Delegate this[object eventKey]
{
[System.Runtime.CompilerServices.MethodImpl(MethodImplOptions.Synchronized)]
get
{
return eventHandlerSet[eventKey];
}
[MethodImpl(MethodImplOptions.Synchronized)]
set
{
eventHandlerSet[eventKey] = value;
}
}
//线程安全的AddHander方法
[MethodImpl(MethodImplOptions.Synchronized)]
public override void AddHandler(object eventKey, Delegate handler)
{
eventHandlerSet.AddHandler(eventKey, handler);
//base.AddHandler(eventKey, handler);
}
//线程安全的RemoveHandler方法
[MethodImpl(MethodImplOptions.Synchronized)]
public override void RemoveHandler(object eventKey, Delegate handler)
{
RemoveHandler(eventKey, handler);
//base.RemoveHandler(eventKey, handler);
}
//线程安全的Fire方法
//该方法首先从events 散列表中找到一个委托链表,然后调用该委托链表上封装的所有的回调方法
/*
* 因为散列表中可能包含有各种各样的不同的委托类型。所以我们不可能在编译进行类型安全
* 的委托调用。因此,我们使用了System.Delegate 的DynamicInvoke方法,交将回调方法的参数
* 组全为一个对象数组传递给它。
* DynamicInvoke方法在内部会检测回调方法期望的参数和我们传递的参数
* 是否匹配。如果匹配,回调方法将被调用
*
* 否则。DynamicInvoke方法将抛出一个异常.
*/
public override void Fire(object eventKey, object sender, EventArgs e)
{
eventHandlerSet.Fire(eventKey, sender, e);
//base.Fire(eventKey, sender, e);
}
}
}
#endregion
#region TypeWithLotsOfEvents类
/*
* 实现步骤
*
* 1.定义一个受保护的实例集合字段
*
* TypeWithLotsOfEvents类型的每个实例都会使用该集合字段来保存其上的事件侦听者
* 集合.这里的的集合通常使用散列表来实现,因为这可以提供比较快的事件查找速度.集合中的第个元素
* 都有一个键(类型通常为System.Object)用以唯一地标识事件类型.以及一个和刍对应的值用以标识事件
* 触发时要调用的委托链表.将该字段的访问限制设为了protected的目的是让派生类型也可以使用集合来为自已定义的一个类型
* ,它在内部使用了一个System.Collections.Hashtable对象.EventHandlerSet 类型还有一些成员负责处理事件和委托实例
*
*
*
* 2.为类型希望提供的事件定义必要的成员
* 对于我们希望类型提供的每个事件.我们必须为其定义一组成员.大家可以已经注意到了
* 我们没有使用实例字段-----它们正是浪费内存的根源
*
* 2.1 构造一个静态只读对象来标识事件
* 我们必须采取一种方式唯一地标识集合中的每一个事件.因为我们使用的是一个散列表.所以我们需要一个
* 唯一的键.这可以由一个对象的散列码产生.
* TypeWithlotsOfEvents类型的所有实例将共享这个惟一的键.所以我们构造了一个Object,并将其引用保存在一个
* 静态只读字段中.只有在TypeWithlotsOfEvents类型的用户构造多个TypeWithLotsOfEvents.这种技巧才会节省更多的内存.
* 如果用户只构造一个TypeWithLotsOfEvents类型实例.这种技巧实际上会耗费更多的内存.如果是这种情况.就不应该使用这里的
* 演示的技巧.另外,本例中将这个静态字段定义为受保护字段.目的是为了让派生类型也能使用它.当然,大家也可以奖其定义为私
* 有字段.
*
* 2.2 为事件定义一个继承自EventArgs的类型,该类型用于保存任何需要传递给事件接受者的附加信息
* 如果事件没有什么特殊的信息需要传递,我们可以简单的使用FCL中的System.EventArgs类型就可以了.本例仅仅是处于演示的目的
* 因此就没有在其中定义任何字段.
*
* 2.3 定义一个委托类型,指定事件触发时被调用的方法原型
* 由于FooEventArgs类型没有定义任何段,所以实际我们不需要定义一个特殊的委托类型.如果事件没有特殊的信息需要传递给侦听者.
* 我们可以直接使用FCL中定义中System.EventHandler类型;
*
* 2.4 显式定义事件及其访问器方法
* 这个整个技艺中比较有趣的部分,在该步骤中,我们为类型希望提供的每一个事件显式定义了add和remove访问器方法
* add 和remove访问器分别调用了EventHandlerSet类中的AddHandler和RemoveHandler方法.EventHandlerSet类型的AddHandler
* 方法会扫描集合看其中是否存在fooEventKey键对象.如果不存在.它将被添加到散列表中.事件被触发时调用的也将是该键对象
* 所标识的委托实例.如果存在,传入的值(新的委托实例)将被组全到已经存在的委托链表上.RemoveHandler方法所做的操作正好相反.
* 在本例中,所有对EventHandlerSet 集合的访问都是线程安全.所以我们不必再在事件的add和remove访问器方法上指定MethodImpAttribute特性
*
* 2.5 定义一个受保护的虚方法,负责通知事件的登记对象
* 不管何时出现"Foo事件",OnFoo方法都会被调用,在本例中,OnFoo方法调用了EventHandlerSet类型的Fire方法,Fire方法会集合中查找传入
* 键对象.如果找到.它将调用与其相关联的委托链表.其中传递给委托链表的参数为this(触发事件的对象)
* 和表示附加信息的继承自EventArgs的类型实例.
*
* 2.6 定义一个方法将输入转化期望的事件
* 本章前面曾经讨论该方法和它的用法.本例中,我们调用了解SimulateFoo方法来模拟出现了一个"Foo事件".该方法所有工作就是
* 传递必要的附加信息.然后触发事件
*
* 3. 为类型希望提供的事件定义必要的成员
*
*
*/
//下面的代码在一个类型中定义多个事件时推荐的设计模式
class TypeWithLotsOfEvents {
//定义一个受保护的实例字段,该字段引用一个集合来管理
//一组事件/委托。注意。类型EventHandlerSet并非们于FCL中,
protected EventHandlerSet eventSet = EventHandlerSet.Syschronized(new EventHandlerSet());
/*
* 2.为Foo事件定义必要的成员.
*
* 2.1 构造一个静态的只读对象来标识该事件.每个对象都有
*
* 一个散列码.用于集合中查找事件对应的委托链表
*
*
*
*/
protected static readonly Object fooEventKey = new object();
/*
* 2.2 为事件定义一个继承自EventArgs类型
*
*
*/
public class FooEventArgs:EventArgs
{
}
/*
*
* 2.3 为事件定义委托原型
*
*/
public delegate void FooEventHandler(Object sender, FooEventArgs e);
/*
* 2.4 为事件定义访问器方法用于在集合上添加/移除委托实例
*
*/
public event FooEventHandler Foo {
add {
eventSet.AddHandler(fooEventKey, value);
}
remove {
eventSet.RemoveHandler(fooEventKey, value);
}
}
/*
* 2.5 为事件定义一个受保护的虚方法(OnXXXX)
*
*/
protected virtual void OnFoo(FooEventHandler e) {
eventSet.Fire(fooEventKey, this, e);
}
/*
* 2.6 定义一个方法将输入转化为期望的事件
*
*/
public void SimulateFoo() {
OnFoo(new FooEventArgs());
}
/*
* 3 为Bar事件定义必要的成员
*
*
* 3.1 构造一个静态只读对象来标识该事件.每个对象都有一个
*
* 散列友用于集合中查找事件对应的委托链表
*/
protected static readonly Object barEventKey = new object();
/*
* 3.2 为事件定义一个继承自EventArgs的类型
*
*/
public class BarEventArgs : EventArgs { }
/*
* 3.3 为事件定义一个委托原型
*/
public delegate void BarEventHander(object sender, BarEventArgs e);
/*
* 3.4 为事件定义一个受保护的虚方法(Onxxx)
*
*
*/
protected virtual void OnBar(BarEventArgs e) {
eventSet.Fire(barEventKey, this, e);
}
//3.5 定义一个方法将输入转化为期望事件
public void SimulateBar() {
OnBar(new BarEventArgs());
}
}
#endregion