.NET事件监听机制的局限与扩展分析

2019-05-23 06:38:56刘景俊

本文实例分析了.NET事件监听机制的局限与扩展。。具体分析如下:

.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)
    {
    }
   
    ... ...