.NET中把“事件”看作一个基本的编程概念,并提供了非常优美的语法支持,对比如下C#和Java代码可以看出两种语言设计思想之间的差异。

// C#
someButton.Click += OnSomeButtonClick;
// Java
someButton.addActionListener(
new ActionListener(){
public void actionPerformed(){
...
}
});

在我们的软件中就大量使用事件来对监听者与发布者解耦,但也遇到了一些局限,在这里跟大家分享一二。一是无法保证监听者的调用顺序;二是当监听者很多时的监听、解除监听的效率问题。

事件监听者的调用顺序

.NET的事件监听机制对监听者的调用顺序没有明确的保证,但有时我们却要求保证不同组件之间的处理顺序。比如,在我们的软件中使用类似解释器模式的方式来实现用户交互操作,一个称作交互源的组件负责将UI控件上的事件分派给一组称为交互器的组件,这些组件依照事先确定的优先级依次获得事件处理的机会,只有当具有高优先级的交互器没有处理事件时,低优先级的组件才能执行进一步的处理。这样,我们就能在不同业务功能的实现中通过以不同的顺序组织交互器来重用它们。比如,重用一些基本的视图缩放、平移、菜单处理等功能。

在上述场景下,如何保证交互器间事件处理的顺序就变得很重要了。当然如果你看一下MulticastDelegate的源代码的话,可以知道在当前的实现中其实各个监听者还是有一定的调用顺序的。但一来这属于实现细节,在将来完全可能改变;二来如果不同的监听器位于不同的模块中时,要依赖于这一实现而保证它们之间的调用顺序也是很困难的。

在这里我们借鉴了Java中以接口进行事件处理的方式,并在添加监听器的同时接收一个表示优先级的参数,这样就可以明确的维护各个监听器的顺序了,如下面的代码所示。我们在交互器(IInteractor)接口中为每一个UI事件定义了相应的方法,并且让InteractSource负责将控件上的事件转化为对接口中相应方法的调用。

public class InteractSource
{
public void AddInteractor(int priority, IInteractor interactor)
{
}
} public interface IInteractor
{
public void OnMouseDown(MouseEventArgs e)
{
} ... ...
}

监听器添加与移除的效率

MulticastDelegate是我们平常使用的事件(event)机制背后的实现,通过其源代码可以看到,它在内部使用数组保存了对各个监听器的引用。这就会造成一个问题——当对一个事件的监听器数目很多时,添加和移除监听器的效率将会变得非常低。以移除为例,对于有N个监听器的事件来说,平均要进行N/2次比较才能确定监听器的位置,而且还要有额外的数组整理操作。为了解决这一情况,我们先是尝试自行定义事件的添加、移除逻辑,并在内部尝试使用字典、哈希表等多种方式进行存储,但事实证明,虽然二者在时间复杂度上有优势,不过其实际效率还是达不到要求。

最好状态下是要有一种能在常数时间内添加和移除监听器的数据结构,也许你也想到了——双向链表。

也许你又想到了——在双向链表中添加和删除是常数时间,但查找却仍然是O(n)的复杂度。

使用接口形式的设计方式再次展现了其灵活性,我们可以将事件发布者的设计为如下形式(示意代码):

public class EventSource
{
private LinkedList list = new LinkedList(); public Tocken AddListener(IEventListener listener)
{
LinkedListNode n = new LinkedListNode(listener);
list.AddLast(n);
return new Tocken(node);
} public void RemoveListener(Tocken tocken)
{
list.Remoe(tocken.node);
} public class Tocken
{
internal LinkedListNode node;
}
}

在此类中使用双向链表存储已经添加的监听器,而在AddListener方法每次调用时都将所添加的链表节点保存到一个令牌(Token)中返回。监听者需要保存这个令牌,并使用它来解除监听。当然,监听者完全可以忽略令牌是个什么东西,就像地铁票从来就是只是一张票而已,我们不曾关心它包含着什么信息。不过对于发布者来说却可以将一些定位信息保存在其中,从而在解除监听时充分利用,在上面的代码中我就保存了链表节点的引用,从而达到监听者的添加、定位、移除都在常数时间内完成。

当然,还可以在Tocken中保存发布者的引用,这样就可以发现”取消对一个从来没有监听过的对象的监听“这样的BUG。或者,还有其它信息。

.NET事件监听机制的局限与扩展的更多相关文章

  1. 4.JAVA之GUI编程事件监听机制

    事件监听机制的特点: 1.事件源 2.事件 3.监听器 4.事件处理 事件源:就是awt包或者swing包中的那些图形用户界面组件.(如:按钮) 事件:每一个事件源都有自己特点有的对应事件和共性事件. ...

  2. 关于事件监听机制的总结(Listener和Adapter)

    记得以前看过事件监听机制背后也是有一种设计模式的.(设计模式的名字记不清了,只记得背后实现的数据结构是数组.) 附上事件监听机制的分析图: 一个事件源可以承载多个事件(只要这个事件源支持这个事件就可以 ...

  3. GUI编程笔记(java)05:GUI事件监听机制原理和举例说明

    1.事件监听机制:       A:事件源          事件发生的地方       B:事件             就是要发生的事情       C:事件处理       就是针对发生的事情做 ...

  4. JAVA事件监听机制学习

    //事件监听机制 import java.awt.*; import java.awt.event.*; public class TestEvent { public static void mai ...

  5. Java中的事件监听机制

    鼠标事件监听机制的三个方面: 1.事件源对象: 事件源对象就是能够产生动作的对象.在Java语言中所有的容器组件和元素组件都是事件监听中的事件源对象.Java中根据事件的动作来区分不同的事件源对象,动 ...

  6. java Gui编程 事件监听机制

    1.     GUI编程引言 以前的学习当中,我们都使用的是命令交互方式: 例如:在DOS命令行中通过javac java命令启动程序. 软件的交互的方式:   1. 命令交互方式    图书管理系统 ...

  7. Java swing(awt):事件监听机制的实现原理+简单示例

    (1)实现原理 事件监听机制的实现: 参考图:事件模型_ActionEvent 为了节省资源,系统无法对某个事件进行实时的监听.故实现的机制是当发生某个事件后,处理代码将被自动运行,类似钩子一般.(回 ...

  8. JAVA之旅(三十一)——JAVA的图形化界面,GUI布局,Frame,GUI事件监听机制,Action事件,鼠标事件

    JAVA之旅(三十一)--JAVA的图形化界面,GUI布局,Frame,GUI事件监听机制,Action事件,鼠标事件 有段时间没有更新JAVA了,我们今天来说一下JAVA中的图形化界面,也就是GUI ...

  9. 简单剖析Node中的事件监听机制(一)

    使用js的class类简单的实现一个事件监听机制,不同于浏览器中的时间绑定与监听,类似于node中的时间监听,并且会在接下来的文章中去根据自己的理解去写一下Event模块中的原理. Node.js使用 ...

随机推荐

  1. 在C#中实现Json的序列化与反序列化

    第一种方式利用 JavaScriptSerializer [对应的Assembly 为 System.Web.Extensions.dll] 进行处理: public static class Kas ...

  2. UWP VirtualizedVariableSizedGridView 支持可虚拟化可变大小Item的View(二)

    上篇UWP VirtualizedVariableSizedGridView 支持可虚拟化可变大小Item的View(一) 讲到该控件的需要和设计过程. 这篇讲讲开发过程中一些重要问题解决. 1.支持 ...

  3. angular使用select时要注意的坑

    一.错误使用产生的坑--留白 公司前段时间要搞一个后台系统,为了快选了angular,在使用select标签的时候碰到一个小问题,首先我们先来看坑图,如图1所示. 如图所示,出现了留白,也就是当我们使 ...

  4. git 用不同的邮箱配置不同的ssh

    首先,介绍一下配置背景: 1.有两个邮箱A@mail.com  , B@mail.com 2.两个git账号,一个公司gitlab上的A@mail ,另一个github上的B@mail 3. 目标:提 ...

  5. FMDB 排它锁

    -------------------------------------基本操作------------------------------------- #import "ViewCon ...

  6. viewgager

    CycleRotationView:自定义控件,主要功能是实现类似与各种商城首页的广告轮播图.其实像这种比较常见的自定义控件早就满大街了,虽然说"不要重复发明轮子",但是不代表不用 ...

  7. 自己用的jquery经常用的工具command

    var Cmd = { Entity: { QueryString: {}, }, RootPath: function () { var pathName = window.location.pat ...

  8. ViewPager+Fragment取消预加载(延迟加载)(转)

    原文:http://www.2cto.com/kf/201501/368954.html 在项目中,都或多或少地使用的Tab布局,所以大都会用到ViewPager+Fragment,但是Fragmen ...

  9. Chrome一直提示“adobe flash player 因过期而遭阻止” ,如何解决?

    完全不用安装最新版的 Chrome,只需要将 Flash 组件安装一下最新版即可. 并且这里需要的不是网上随处可见的 NPAPI 版本,而是冷门的 PPAPI 版本. 请收藏好这个链接,是某位大牛从 ...

  10. Thisgood

    this is good test print 'haha'