博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
.NET 事件模型教程(二)
阅读量:5931 次
发布时间:2019-06-19

本文共 9124 字,大约阅读时间需要 30 分钟。

[日期:2005-01-22] 来源:  作者:破宝 [字体:大 中 小]

目录

属性样式的事件声明

在第一节中,我们讨论了 .NET 事件模型的基本实现方式。这一部分我们将学习 C# 语言提供的高级实现方式:使用 add/remove 访问器声明事件。(注:本节内容不适用于 VB.NET。)

我们再来看看上一节中我们声明事件的格式:

public event [委托类型] [事件名称];

这种声明方法,类似于类中的字段(field)。无论是否有事件处理程序挂接,它都会占用一定的内存空间。一般情况中,这样的内存消耗或许是微不足 道的;然而,还是有些时候,内存开销会变得不可接受。比如,类似 System.Windows.Forms.Control 类型具有五六十个事件,这些事件并非每次都会挂接事件处理程序,如果每次都无端的多处这么多的内存开销,可能就无法容忍了。

好在 C# 语言提供了“属性”样式的事件声明方式:

public event [委托类型] [事件名称]        {            add { .... }            remove { .... }        }

如上的格式声明事件,具有 add 和 remove 访问器,看起来就像属性声明中的 get 和 set 访问器。使用特定的存储方式(比如使用 Hashtable 等集合结构),通过 add 和 remove 访问器,自定义你自己的事件处理程序添加和移除的实现方法。

Demo 1G:“属性”样式的事件声明。我首先给出一种实现方案如下(此实现参考了 .NET Framework SDK 文档中的一些提示)(限于篇幅,我只将主要的部分贴在这里):

public delegate void StartWorkEventHandler(object sender, StartWorkEventArgs e);        public delegate void RateReportEventHandler(object sender, RateReportEventArgs e);        // 注意:本例中的实现,仅支持“单播事件”。        // 如需要“多播事件”支持,请参考 Demo 1H 的实现。        // 为每种事件生成一个唯一的 object 作为键        static readonly object StartWorkEventKey = new object();        static readonly object EndWorkEventKey = new object();        static readonly object RateReportEventKey = new object();        // 使用 Hashtable 存储事件处理程序        private Hashtable handlers = new Hashtable();        // 使用 protected 方法而没有直接将 handlers.Add / handlers.Remove        // 写入事件 add / remove 访问器,是因为:        // 如果 Worker 具有子类的话,        // 我们不希望子类可以直接访问、修改 handlers 这个 Hashtable。        // 并且,子类如果有其他的事件定义,        // 也可以使用基类的这几个方法方便的增减事件处理程序。        protected void AddEventHandler(object eventKey, Delegate handler)        {            lock(this)            {                if (handlers[ eventKey ] == null)                {                    handlers.Add( eventKey, handler );                }                else                {                    handlers[ eventKey ] = handler;                }            }        }        protected void RemoveEventHandler(object eventKey)        {            lock(this)            {                handlers.Remove( eventKey );            }        }        protected Delegate GetEventHandler(object eventKey)        {            return (Delegate) handlers[ eventKey ];        }        // 使用了 add 和 remove 访问器的事件声明        public event StartWorkEventHandler StartWork        {            add { AddEventHandler(StartWorkEventKey, value); }            remove { RemoveEventHandler(StartWorkEventKey); }        }        public event EventHandler EndWork        {            add { AddEventHandler(EndWorkEventKey, value); }            remove { RemoveEventHandler(EndWorkEventKey); }        }                public event RateReportEventHandler RateReport        {            add { AddEventHandler(RateReportEventKey, value); }            remove { RemoveEventHandler(RateReportEventKey); }        }        // 此处需要做些相应调整        protected virtual void OnStartWork( StartWorkEventArgs e )        {            StartWorkEventHandler handler =                 (StartWorkEventHandler) GetEventHandler( StartWorkEventKey );            if (handler != null)            {                handler(this, e);            }        }        protected virtual void OnEndWork( EventArgs e )        {            EventHandler handler =                (EventHandler) GetEventHandler( EndWorkEventKey );            if (handler != null)            {                handler(this, e);            }        }        protected virtual void OnRateReport( RateReportEventArgs e )        {            RateReportEventHandler handler =                (RateReportEventHandler) GetEventHandler( RateReportEventKey );            if (handler != null)            {                handler(this, e);            }        }        public Worker()        {        }        public void DoLongTimeTask()        {            int i;            bool t = false;            double rate;            OnStartWork(new StartWorkEventArgs(MAX) );            for (i = 0; i <= MAX; i++)            {                Thread.Sleep(1);                t = !t;                rate = (double)i / (double)MAX;                OnRateReport( new RateReportEventArgs(rate) );            }            OnEndWork( EventArgs.Empty );        }

细细研读这段代码,不难理解它的算法。这里,使用了名为 handlers 的 Hashtable 存储外部挂接上的事件处理程序。每当事件处理程序被“add”,就把它加入到 handlers 里存储;相反 remove 时,就将它从 handlers 里移除。这里取 event 的 key (开始部分为每一种 event 都生成了一个 object 作为代表这种 event 的 key)作为 Hashtable 的键。

[]

 

单播事件和多播事件

在 Demo 1G 给出的解决方案中,你或许已经注意到:如果某一事件被挂接多次,则后挂接的事件处理程序,将改写先挂接的事件处理程序。这里就涉及到一个概念,叫“单播事件”。

所谓单播事件,就是对象(类)发出的事件通知,只能被外界的某一个事件处理程序处理,而不能被多个事件处理程序处理。也就是说,此事件只能被挂接一次,它只能“传播”到一个地方。相对的,就有“多播事件”,对象(类)发出的事件通知,可以同时被外界不同的事件处理程序处理。

打个比方,上一节开头时张三大叫一声之后,既招来了救护车,也招来了警察叔叔(问他是不是回不了家了),或许还有电视转播车(现场直播、采访张三为什么大叫,呵呵)。

多播事件会有很多特殊的用法。如果以后有机会向大家介绍 Observer 模式,可以看看 Observer 模式中是怎么运用多播事件的。(注:经我初步测试,字段形式的事件声明,默认是支持“多播事件”的。所以如果在事件种类不多时,我建议你采用上一节中所讲 的字段形式的声明方式。)

[]

 

支持多播事件的改进

Demo1H,支持多播事件。为了支持多播事件,我们需要改进存储结构,请参考下面的算法:

public delegate void StartWorkEventHandler(object sender, StartWorkEventArgs e);        public delegate void RateReportEventHandler(object sender, RateReportEventArgs e);        // 为每种事件生成一个唯一的键        static readonly object StartWorkEventKey = new object();        static readonly object EndWorkEventKey = new object();        static readonly object RateReportEventKey = new object();        // 为外部挂接的每一个事件处理程序,生成一个唯一的键        private object EventHandlerKey        {            get { return new object(); }        }        // 对比 Demo 1G,        // 为了支持“多播”,        // 这里使用两个 Hashtable:一个记录 handlers,        // 另一个记录这些 handler 分别对应的 event 类型(event 的类型用各自不同的 eventKey 来表示)。        // 两个 Hashtable 都使用 handlerKey 作为键。        // 使用 Hashtable 存储事件处理程序        private Hashtable handlers = new Hashtable();        // 另一个 Hashtable 存储这些 handler 对应的事件类型        private Hashtable events = new Hashtable();        protected void AddEventHandler(object eventKey, Delegate handler)        {            // 注意添加时,首先取了一个 object 作为 handler 的 key,            // 并分别作为两个 Hashtable 的键。            lock(this)            {                object handlerKey = EventHandlerKey;                handlers.Add( handlerKey, handler );                events.Add( handlerKey, eventKey);            }        }        protected void RemoveEventHandler(object eventKey, Delegate handler)        {            // 移除时,遍历 events,对每一个符合 eventKey 的项,            // 分别检查其在 handlers 中的对应项,            // 如果两者都吻合,同时移除 events 和 handlers 中的对应项。            //            // 或许还有更简单的算法,不过我一时想不出来了 :(            lock(this)            {                foreach ( object handlerKey in events.Keys)                {                    if (events[ handlerKey ] == eventKey)                    {                        if ( (Delegate)handlers[ handlerKey ] == handler )                        {                            handlers.Remove( handlers[ handlerKey ] );                            events.Remove( events[ handlerKey ] );                            break;                        }                    }                }            }        }        protected ArrayList GetEventHandlers(object eventKey)        {            ArrayList t = new ArrayList();            lock(this)            {                foreach ( object handlerKey in events.Keys )                {                    if ( events[ handlerKey ] == eventKey)                    {                        t.Add( handlers[ handlerKey ] );                    }                }            }            return t;        }        // 使用了 add 和 remove 访问器的事件声明        public event StartWorkEventHandler StartWork        {            add { AddEventHandler(StartWorkEventKey, value); }            remove { RemoveEventHandler(StartWorkEventKey, value); }        }        public event EventHandler EndWork        {            add { AddEventHandler(EndWorkEventKey, value); }            remove { RemoveEventHandler(EndWorkEventKey, value); }        }                public event RateReportEventHandler RateReport        {            add { AddEventHandler(RateReportEventKey, value); }            remove { RemoveEventHandler(RateReportEventKey, value); }        }        // 此处需要做些相应调整        protected virtual void OnStartWork( StartWorkEventArgs e )        {            ArrayList handlers = GetEventHandlers( StartWorkEventKey );            foreach(StartWorkEventHandler handler in handlers)            {                handler(this, e);            }        }        protected virtual void OnEndWork( EventArgs e )        {            ArrayList handlers = GetEventHandlers( EndWorkEventKey );            foreach(EventHandler handler in handlers)            {                handler(this, e);            }        }        protected virtual void OnRateReport( RateReportEventArgs e )        {            ArrayList handlers = GetEventHandlers( RateReportEventKey );            foreach(RateReportEventHandler handler in handlers)            {                handler(this, e);            }        }

上面给出的算法,只是给你做参考,应该还有比这个实现更简单、更高效的方式。

为了实现“多播事件”,这次使用了两个 Hashtable:一个存储“handlerKey - handler”对,一个存储“handlerKey - eventKey”对。相信通过仔细研读,你可以读懂这段代码。我就不再赘述了。

引用自 :

转载于:https://www.cnblogs.com/zhangchenliang/archive/2012/08/30/2662974.html

你可能感兴趣的文章
我的友情链接
查看>>
python 利用random的shuffle洗牌方法生产1个12位数的随机密码
查看>>
MySQL存储引擎--MyISAM与InnoDB区别
查看>>
Android音频开发(4):如何存储和解析wav文件
查看>>
Tomcat乱码解决方法
查看>>
图的顶点间最小路径问题
查看>>
JDK8环境变配置量
查看>>
20个非常有用的Java程序片段(一)
查看>>
使用jQuery.form.js/springmvc框架实现文件上传功能
查看>>
Java调用python脚本
查看>>
VS2017安装ReSharper
查看>>
copy 的实现原理与深浅拷贝
查看>>
Redis+Tomcat+Nginx实现session共享
查看>>
[转]
查看>>
Eclipse 工作集 Task 搜索
查看>>
Install OpenStack Kilo
查看>>
php面向对象详解4
查看>>
vi或者vim文件加密和乱码的处理
查看>>
RXJava异步代码
查看>>
AD维护管理工具详解(二)netdiag
查看>>