• EventBroker:同步和异步通知组件,松散耦合的事件处理


    Please see the Download section below for instructions on how to run the code.

    前言 本文是对Daniel Grunwald的文章《c#中的弱事件》的回应。 介绍 EventBroker是一个可以用于在系统中触发和接收通知的组件。 特性 松耦合:订阅者不必知道发布者。它们都只需要知道EventTopic URI(系统中唯一标识EventTopic的字符串)。这有助于构建松散耦合的系统。 线程同步:订阅者定义在哪个线程上执行订阅处理程序: 与发布服务器(同步)后台线程(异步)相同的线程用户界面线程(同步或异步) 发布者可以将订阅限制为同步或异步的事件。 多个发布者/订阅者:多个发布者/订阅者可以触发/处理同一个事件主题。 弱引用:弱引用引用发布者和订阅者,这不会阻止它们被垃圾收集——就像在正常的事件注册模型中一样。 范围: 每个EventBroker的作用域:在触发事件时,只有注册在同一个EventBroker上的发布者和订阅者被连接在一起。通常,您将在系统中使用一个EventBroker来处理所有事件通知。但是,在特殊情况下,为子系统定义一个新的EventBroker是有用的。这使您可以为事件通知定义范围。 具有层次命名的作用域:发布者和订阅者可以以层次方式命名,事件可以是全局的,只对父事件或只对子事件。发布者和订阅者都可以定义它们发布/接收的范围。 背景 这个EventBroker基于来自Microsoft的复合(UI)应用程序块的EventBroker。请参阅下面与CAB EventBroker的比较小节,以了解不同之处。 使用的代码 样本的出版商 发布事件主题: 隐藏,复制Code

    public class Publisher
    {
        [EventPublication("topic://EventBrokerSample/SimpleEvent")]
        public event EventHandler SimpleEvent;
        
        ///<summary>Fires the SimpleEvent</summary>
        public void CallSimpleEvent()
        {
            SimpleEvent(this, EventArgs.Empty);
        }
    }

    向事件代理注册发布者(您必须在代码中保存事件代理的实例)。示例假设有一个为我们保存事件代理实例的服务: 隐藏,复制Code

    EventBroker eb = Service.EventBroker;
    Publisher p = new Publisher();
    eb.Register(p);

    在注册发布者时,事件代理检查发布者是否发布了事件(具有EventPublication属性的事件)。 示例用户 订阅一个事件主题: 隐藏,复制Code

    public class Subscriber
    {
        [EventSubscription(
            "topic://EventBrokerSample/SimpleEvent", 
            typeof(Handlers.Publisher))]
        public void SimpleEvent(object sender, EventArgs e)
        {
            // do something useful or at least funny
        }
    }

    向事件代理注册订阅者: 隐藏,复制Code

    EventBroker eb = Service.EventBroker; 
    Subscriber s = new Subscriber();
    eb.Register(s); 

    事件代理将在注册时检查订阅服务器是否订阅了事件主题(具有EventSubscription属性的方法)。 如果发布者为已注册的订阅者触发事件主题,则事件代理将通过使用发布者用于触发其事件的sender和Eventargs调用订阅处理程序方法将其转发给订阅者。 出版选项 简单的 隐藏,复制Code

    [EventPublication("Simple")]
    public event EventHandler SimpleEvent;

    使用自定义的Eventargs 注意:CustomEventArgs只能从EventArgs派生。 隐藏,复制Code

    [EventPublication("CustomEventArgs")]
    public event EventHandler<CustomEventArguments> CustomEventArgs;

    使用单个事件发布多个事件主题 隐藏,复制Code

    [EventPublication("Event1")]
    [EventPublication("Event2")]
    [EventPublication("Event3")]
    public event EventHandler MultiplePublicationTopics;

    只允许同步订阅处理程序 有关更多细节,请参阅与CAB EventBroker/订阅处理程序限制的比较部分。 隐藏,复制Code

    [EventPublication("test", HandlerRestriction.Synchronous)]
    public event EventHandler AnEvent; 

    只允许异步订阅处理程序 有关更多细节,请参阅与CAB EventBroker/订阅处理程序限制的比较一节。 隐藏,复制Code

    [EventPublication("test", HandlerRestriction.Asynchronous)]
    public event EventHandler AnEvent;

    订阅选项 简单的 隐藏,复制Code

    [EventSubscription("Simple", typeof(Handlers.Publisher)]
    public void SimpleEvent(object sender, EventArgs e) {} 

    定制的Eventargs 隐藏,复制Code

    [EventSubscription("CustomEventArgs"), typeof(Handlers.Publisher))]
    public void CustomEventArgs(object sender, CustomEventArgs e) {} 

    订阅多个事件主题 隐藏,复制Code

    [EventSubscription("Event1", typeof(Handlers.Publisher))]
    [EventSubscription("Event2", typeof(Handlers.Publisher))]
    [EventSubscription("Event3", typeof(Handlers.Publisher))]
    public void MultipleSubscriptionTopics(object sender, EventArgs e) {} 

    在后台线程上执行处理器(异步) 事件代理创建一个工作线程来执行处理程序方法。发布者可以立即继续处理。 隐藏,复制Code

    [EventSubscription("Background", typeof(Handlers.Background))]
    public void BackgroundThread(object sender, EventArgs e) {} 

    在UI线程上执行处理器 如果从后台工作线程调用更新用户界面的用户界面组件,则使用此选项—不再需要Control.Invoke(…)。 隐藏,复制Code

    [EventSubscription("UI", typeof(Handlers.UserInterface))]
    public void UI(object sender, EventArgs e) {}

    注意,如果使用UserInterface处理程序,那么必须确保在用户接口线程上注册了订阅者。否则,EventBroker将无法切换到用户界面线程,并将抛出异常。 在UI线程上异步执行处理程序 与上面相同,但在订阅服务器处理完事件之前,不会阻塞发布服务器。 隐藏,复制Code

    [EventSubscription("UIAsync", typeof(Handlers.UserInterfaceAsync))]
    public void UI(object sender, EventArgs e) {}  

    直接在EventBroker上触发事件主题 事件主题可以直接在EventBroker上触发,而不需要注册发布者。当您需要从只存在很短时间的对象触发事件主题时,这就很方便了。 这种场景的一个很好的示例是计划作业执行。在计划时间,将实例化并执行作业类的实例。注册它会很麻烦实例,触发事件,然后再次取消注册-直接在EventBroker上触发事件要容易得多: 隐藏,复制Code

    eventBroker.Fire("topic", sender, eventArgs);

    范围 有时,有必要限制发布事件的范围。这可以通过两种不同的方式实现: 事件代理的多个实例 订阅者只能监听注册在同一事件代理上的发布者的事件。因此,事件代理会自动构建范围。 如果应用程序中有多个事件处理范围,这是最简单的解决方案,应该始终是首选方案。但是,有时您需要在单个事件代理上对对象的作用域进行更多的控制。下一节将描述此场景。 分层命名 发布者和订阅者可以通过实现INamedItem接口来命名。这个接口提供了一个属性: 隐藏,复制Code

    string EventBrokerItemName { get; }

    这允许在事件代理中标识对象,而发布和订阅属性始终绑定到类。 命名采用与名称空间相同的模式进行分层: Test是Test. mypublisher1test的父类。MyName1是Test.MyName2Test.MyName1的兄弟姐妹。子名称是Test.MyName1Test的子元素。MyName1是Test的孪生兄弟。MyName1(名称相同的两个对象是双胞胎) 现在,发布者可以定义它想要发布事件的范围,方法是在publication属性中定义一个范围: 隐藏,复制Code

    [EventPublication("Topic")]
    public event EventHandler PublishedGlobally;
    
    [EventPublication("Topic", typeof(ScopeMatchers.PublishToParents)]
    public event EventHandler PublishedToParentsAndTwinsOnly;
    
    [EventPublication("Topic", typeof(ScopeMatchers.PublishToChildren)]
    public event EventHandler PublishedToChildrenAndTwinsOnly;

    第一个事件是所有订阅者都可以接收的全局事件。第二个事件仅传递给发布者的父或孪生订阅者。第三个事件仅传递给与发布者相邻或相邻的订阅者。 订阅者可以定义它想要接收事件的范围: 隐藏,复制Code

    [EventSubscription("Topic", typeof(Handlers.Publisher)]
    public void GlobalHandler(object sender, EventArgs e)
    
    [EventSubscription(
        "Topic", 
        typeof(Handlers.Publisher), 
        typeof(ScopeMatchers.SubscribeToParents)]
    public void ParentHandler(object sender, EventArgs e)
    
    [EventSubscription(
        "Topic", 
        typeof(Handlers.Publisher), 
        typeof(ScopeMatchers.SubscribeToChildren)]
    public void ChildrenHandler(object sender, EventArgs e)

    第一个订阅是全局订阅,它将处理传递给它的所有事件。第二个订阅仅在订阅服务器的父订阅服务器触发事件时调用。只有当订阅者的子订阅触发事件时,才会调用第三个订阅。 双胞胎(具有相同名称的不同对象)被特殊处理。双胞胎总是它的双胞胎的父和子,因此将总是从它的双胞胎接收所有事件。 与CAB EventBroker的比较 如背景部分所述,我的EventBroker基于来自Microsoft的实践和模式组CAB(复合UI应用程序块)的EventBroker。 本节描述它们的区别。 独立的 bbv。Common EventBroker可以独立使用。您可以在项目中任何需要通知的地方使用它,而不像CAB要求的那样有任何框架约束。 开发人员指南 我尝试以一种能尽快看到错误的方式实现EventBroker。我会给你一些例子来说明它的含义: 如果已发布事件提供的EventHandler类型与订阅处理程序提供的签名不匹配,则会在注册时而不是在事件触发时抛出异常。如果你使用UserInterface或UserInterfaceAsync订阅处理程序,那么一个异常抛出在注册时,如果没有WindowsFormsSynchronizationContext是存在的,这意味着当前线程不是用户界面线程。这将导致在处理事件时出现跨线程异常。 订阅处理程序的限制 注意:这个特性只在Sourceforge.net上的版本中可用(参见下载部分),而在附加的解决方案中不可用(它太新了;-))。 我们遇到的问题是,一些发布者必须确保所有订阅者同步地处理其事件。例如,如果您发布了一个取消事件,则为所有订阅者提供了一种取消当前操作的方法。只有当没有订阅者将CancelEventArgs上的Cancel属性设置为true时,发布者才能继续其操作。因此,发布者必须将此事件上的所有订阅限制为同步: 隐藏,复制Code

    [EventPublication("test", HandlerRestriction.Synchronous)]
    public event EventHandler AnEvent;

    如果订阅者为此事件注册异步处理程序,则抛出异常。注意,异常是在注册时抛出的,而不是在触发事件时抛出的。这大大简化了一致代码的编写。 此外,发布者可以将订阅处理程序限制为异步的,因为发布者不想被阻塞: 隐藏,复制Code

    [EventPublication("test", HandlerRestriction.Asynchronous)]
    public event EventHandler AnEvent;

    日志记录 bbv的事件代理。Common在日志消息方面非常健谈。这使您能够了解发布者何时触发事件、如何将事件路由到订阅者、如何处理事件以及发生了哪些异常。 注意:bbv。通常使用log4net记录消息。这意味着,您可以为每个组件配置记录的消息级别。 可扩展性 ThreadOption——比;处理程序 我用可扩展的处理程序替换了enum ThreadOption来定义事件的处理方式(同步)hronously异步)。 范围——比;匹配器范围 您可以实现自己的范围匹配器来提供符合您需要的事件层次结构,而不是使用枚举指定范围。 内部 我们将在本文的下一个更新中了解其内部原理。在此之前,请参阅下载中提供的源代码。 下载 本文顶部的下载包含三个Visual Studio 2008项目: bbv.Common。事件代理组件。bbv. common .EventBroker。测试:单元测试(NUnit).bbv. common . eventbroker。示例:一个小示例应用程序(事件代理的基本用法)。 bbv.Common。EventBroker是一个更大的库的一部分,其中包含一些其他很酷的组件,这些组件可以在这里找到。 这个下载只是一个精简版。要试用它,请打开解决方案,将启动项目设置为bb . common . eventbroker。取样,然后按F5。 请注意,本文附带的版本并不是最新版本。请务必查看Sourceforge页面以了解最新版本。 许可证 注意,bbc . common。EventBroker是在Apache License 2.0下许可的,但是因为EventBroker包含来自Microsoft的CAB的部分,所以必须应用额外的许可限制,详细信息请参阅源代码中的文件头。 历史 更新:阅读:初始版本。更新:阅读:添加了与CAB EventBroker的比较,添加了sourceforge.net的链接。 本文转载于:http://www.diyabc.com/frontweb/news1995.html

  • 相关阅读:
    【转载】实时嵌入式软件开发的25个常见错误
    软件测试高级测评师培训-白盒测试所用工具
    SQL update select语句
    JS对URL字符串进行编码/解码分析
    Java利用jacob实现文档格式转换
    Web打印--Lodop API
    常用正则表达式
    截取视频第一帧作为预览图片
    plupload api
    URL特殊字符的转义
  • 原文地址:https://www.cnblogs.com/Dincat/p/13456822.html
Copyright © 2020-2023  润新知