本节导读:
本节说明了如何使用EventAggregator提供模块间的通信。
2012-2-3

9.4 事件聚合器

Prism提供了事件机制以拿模块间的松耦合交流成为可能。在这个机制中,基于事件聚合服务,订阅者和发布者可以在不互相建立引用的情况下进行通信。

EventAggregator提供了多路广播发布者/订阅者的功能。也就是说多个发布者可以发布同一个事件或者同一个事件可以被多个订阅者所监听。考虑使用EventAggregator发布事件以确保消息可以在如控制器或者表现者之类的业务逻辑代码间发送。

举例而言,在the Stock Trader RI中,当Process Order按钮被按下并且订单成功处理;在这种情况下,其它模块需要知道订单已经被成功提交以使它们可以更新自己的View。

由Prism创建是事件是强类型事件,也就是说编译时类型检查会应用在事件上这使得程序可以在运行之前发现错误。在Prism中,EventAggregator允许订阅者或者发布者指定一个特定的EventBase。事件聚合器适用于多个发布者和多个订阅者,如下图所示。

【注意】:关于.NET Framework事件

使用.NET Framework的事件是在不需要松耦合的情况下跨模块通信最简便和最直接的方法。.NET Framework的事件实现了发布者-订阅者模式,但是需要订阅一个对象的事件就需要直接引用这个对象,而在复杂程序中,这个对象往往在其它模块里。这将会导致一个紧耦合的设计。因此,.NET Framework的事件被用于模块内交流而不是模块间的交流。

当使用.NET Framework的事件时要非常小心内存泄漏,特别时当静态类或者生命周期长的组件订阅了非静态类或者生命周期短的组件的事件时。如果订阅者不取消订阅,那么发布者就一直保持有效状态,从而导致垃圾回收机制无法回收发布者。

9.4.1 IEventAggregator

EventAggregator在容器中提供了一个可以通过IEventAggregator接口检索的服务。事件聚合器负责收集和定位事件并且在系统中保存一个事件集合。

public interface IEventAggregator

{       

   TEventType GetEvent<TEventType>() where TEventType : EventBase;

}

EventAggregator没有构造时,如果被访问则构造一个事件。这个操作会使事件发布者和订阅者从需要监视的状态中移除,该操作无论事件是否可用。([This 代词,指EventAggregator] [relieves < the publisher or subscriber 发布者或者订阅者> from <needing to determine需要监视,现在分词needing表主动,表示系统正在监视的内容>将XXX从XXX中移除] [whether the event is available无论事件是否可用].)

9.4.2 CompositePresentationEvent

连接发布者和订阅者过程的实际操作者是CompositePresentationEvent类。它是在Prism中唯一的EventBase的实现。该类维护订阅者列表并且将事件发布到订阅者手中。

CompositePresentationEvent是一个泛类,作为泛类,定义时就需要一个负载类型。这有助于在编译时检查订阅者和发布者之间提供了正确的方法来保证事件的连接。以下代码是CompositePresentationEvent的一部分定义。

public class CompositePresentationEvent<TPayload> : EventBase

{

    ...

public SubscriptionToken Subscribe(Action<TPayload> action);

public SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption);

public SubscriptionToken Subscribe(Action<TPayload> action, bool keepSubscriberReferenceAlive)

public virtual SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption, bool keepSubscriberReferenceAlive);

public virtual SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption, bool keepSubscriberReferenceAlive, Predicate<TPayload> filter);

public virtual void Publish(TPayload payload);

public virtual void Unsubscribe(Action<TPayload> subscriber);

public virtual bool Contains(Action<TPayload> subscriber)

    ...

}

9.4.3 创建并且发布事件

下文描述了如何通过IEventAggregator接口创建,发布,和订阅CompositePresentationEvent

1)创建事件

CompositePresentationEvent<TPayload>的设计意图是成为应用程序或者模块特定事件的基类。TPayLoad是事件负载内容的类型。负载内容指的是事件发布后需要传递给订阅者的参数。

比如,以下代码是the Stock Trader Reference Implementation (Stock Trader RI)中的TickerSymbolSelectedEvent。其中负载内容就是包含Company Symbol的字符串。注意,该类的实现是空的。

public class TickerSymbolSelectedEvent : CompositePresentationEvent<string>{}

【注意】:在复杂项目中,事件需要在多个模块中频繁使用,所以将它们定义在共用的地方。在the Stock Trader RI中,定义在StockTraderRI.Infrastructure(译注,Infrastructure是基础结构的意思)项目里。

2)发布事件

发布者通过EventAggregator查到事件并且调用Publish方法发布。在构造函数中加入一个IEventAggregator类型的参数并且使用依赖注入就可以访问EventAggregator了。

下面的例子说明了发布TickerSymbolSelectedEvent的方法。

this.eventAggregator.GetEvent<TickerSymbolSelectedEvent>().Publish("STOCK0");

9.4.3 订阅事件

订阅者通过使用CompositePresentationEvent类中提供的Subscribe方法的某个可用重载订阅事件。订阅CompositePresentationEvents有许多方法,下面的标准可以帮助您决定。

l  如果事件触发时需要更新UI元素,那就在UI线程中订阅。

l  如果需要过滤事件,在订阅时提供一个筛选的委托。

l  如果需要关注事件的性能,在订阅时使用强委托引用并且之后再手动的取消订阅CompositePresentationEvent

l  如果上述情况都不适用,那就使用默认配置。

下文描述了这些情况。

1)在UI线程中订阅

通常,订阅者在响应事件时需要更新在UI元素。在WPF和Silverlight中,只有UI线程才能更新UI元素。

默认情况下,订阅者在发布者的线程中收到事件。如果发布者在UI线程中发送事件,那订阅者就可以更新UI元素。然而,如果发布者的线程是一个后台线程,那订阅者就不能直接更新UI元素了。在这种情况下,订阅者就需要使用Dispatcher类以按预定时间更新UI元素。

Prism提供的CompositePresentationEvent类帮助订阅者自动在UI线程中接收事件。订阅者只要在订阅时说明这个情况就行了。

public void Run()

{

   ...

this.eventAggregator.GetEvent<TickerSymbolSelectedEvent>().Subscribe(ShowNews, ThreadOption.UIThread);

);

}

public void ShowNews(string companySymbol)

{

this.articlePresentationModel.SetTickerSymbol(companySymbol);

}


ThreadOption
提供了以下选项:

PublisherThread。该设定使事件在发布者的线程中接收,这是默认设置。

BackgroundThread。该设定则会在.NET Framework线程池中线程上异步接收事件。

UIThread。该设定会在UI线程上接收事件。

2)订阅的筛选

订阅者可能不需要处理已发布事件的所有对象。在这种情况下,订阅者可以使用filter参数。filterSystem.Predicate<TPayLoad>类型的参数,它是通过判断负载内容是否满足一系列条件以确定事件是否需要订阅者响应的委托。如果负载内容不满足条件,那订阅者的回调函数就不会执行。

通常,筛选器都由一个Lambda表达式提供,如下所示。

FundAddedEvent fundAddedEvent = this.eventAggregator.GetEvent<FundAddedEvent>();

fundAddedEvent.Subscribe(FundAddedEventHandler, ThreadOption.UIThread, false,

fundOrder => fundOrder.CustomerId == this.customerId);

【注意】:Silverlight不支持弱引用的Lambda表达式或者匿名委托。

在Silverlight中,就需要调用一个独立的公用方法,如下所示。

public bool FundOrderFilter(FundOrder fundOrder)

{

return fundOrder.CustomerId == this.customerId;

}

...

FundAddedEvent fundAddedEvent = this.eventAggregator.GetEvent<FundAddedEvent>();

subscriptionToken = fundAddedEvent.Subscribe(FundAddedEventHandler, ThreadOption.UIThread, false, FundOrderFilter);

【注意】:Subscribe返回一个类型为Microsoft.Practices.Prism.Events.SubscriptionToken的订阅令牌,该令牌可以在之后提供移除事件订阅的功能。该令牌在回调函数为匿名委托或者Lambda表达式或者通过不同筛选器订阅同一个事件是非常有用。

【注意】:在事件回调函数中不建议修改负载对象因为会有多个线程同时文章负载对象。您必须保证负载对象不变以避免并发错误。

3)通过强委托引用订阅

如果在短时间内触发多个需要注意性能的事件,可能需要将它们以强委托引用的方式订阅。以这种方式订阅的事件需要在释放订阅者的时候手动取消订阅事件。

在默认情况下,CompositePresentationEvent在订阅时维持着订阅者和筛选器的弱委托引用。也就是说CompositePresentationEvent中的引用不会阻止垃圾回收机制回收订阅者。使用弱委托引用使订阅者不需要解除订阅,也允许垃圾回收。

当然,维护一个弱委托引用比一般的强引用要慢一些。在大多数应用程序中,性能不需要特别注意,但是如果应用程序在短时间内发布了大量事件,就需要在CompositePresentationEvent中使用强引用。如果使用了强委托引用,那么在订阅对象不需要使用的时候需要反订阅事件以使本身的垃圾回收可以作用于订阅对象。

要订阅强引用事件,在Subscribe方法中使用keepSubscriberReferenceAlive参数,如下所示。

FundAddedEvent fundAddedEvent = eventAggregator.GetEvent<FundAddedEvent>();

bool keepSubscriberReferenceAlive = true;

fundAddedEvent.Subscribe(FundAddedEventHandler, ThreadOption.UIThread, keepSubscriberReferenceAlive, fundOrder => fundOrder.CustomerId == _customerId);


keepSubscriberReferenceAlive
参数是bool类型的。

l  当设置为true时,事件实例就会保持对订阅者的强引用,从而使订阅者不会被回收。关于如何取消订阅,参见稍后提及的“取消订阅事件”一节。

l  当设置为false(这是参数省略时的默认值),事件就维持订阅者对象的弱引用,这样允许垃圾回收在订阅者对象不再被其它对象引用时自动翻译对象。当订阅者被回收时,事件自动取消订阅。

4)默认订阅

在使用默认值订阅时,订阅者必需提供一个拥有正确函数原型的回调方法以接收事件通知。比如,TickerSymbolSelectedEvent的处理者就需要有一个字符串型的参数,如下所示。

public void Run()

{

   ...

this.eventAggregator.GetEvent<TickerSymbolSelectedEvent>().Subscribe(ShowNews);

}

public void ShowNews(string companySymbol)

{

   articlePresentationModel.SetTickerSymbol(companySymbol);

}

5)取消订阅事件

如果订阅者不再需要接收事件时,可以使用订阅者或者订阅信息取消订阅。

下面的例子说明了如何直接取消订阅。

FundAddedEvent fundAddedEvent = this.eventAggregator.GetEvent<FundAddedEvent>();

fundAddedEvent.Subscribe(FundAddedEventHandler, ThreadOption.PublisherThread);

fundAddedEvent.Unsubscribe(FundAddedEventHandler);

下面的例子说明了如何使用订阅令牌取消订阅。这个令牌是由Subscribe方法返回的。

FundAddedEvent fundAddedEvent = this.eventAggregator.GetEvent<FundAddedEvent>();

subscriptionToken = fundAddedEvent.Subscribe(FundAddedEventHandler, ThreadOption.UIThread, false, fundOrder => fundOrder.CustomerId == this.customerId);

fundAddedEvent.Unsubscribe(subscriptionToken);

转载于:https://www.cnblogs.com/Pray4U/archive/2012/02/03/2337597.html

Prism4文档翻译(第九章 第二部分)相关推荐

  1. kurento 6.14.0文档翻译第九章 编写Kurento应用程序

    目录 编写Kurento应用程序 全局架构 通讯客户端,服务器和Kurento 媒体协商阶段(信令) 媒体交换阶段 使用实时的WebRTC应用 媒体平面 9.1全局架构 可以按照网络的架构原理使用Ku ...

  2. 第二集 第一魂环 第九章

    第二集 第一魂环 第九章 蓝银草的第一魂环(一) "魂师的修炼,在二十岁之前是一个极为重要的过程,可以说,二十岁以前的成绩,决定了未来的成就.三十级,是一个冰火两重天的门槛.二十岁以前,跨过 ...

  3. 信息学奥赛一本通(C++版) 第二部分 基础算法 第九章 动态规划

    总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 第二部分 基础算法 第九章 动态规划 第一节 动 ...

  4. 深入理解Java虚拟机(第二版) 第九章:类加载及执行子系统的案例与实战

    第九章 类加载及执行子系统的案例与实战 9.1 概述 9.2 Tomcat: 正统的类加载器架构 9.3 OSGi:灵活的类加载器架构 9.4 字节码生成技术与动态代理的实现 9.5 Retrotra ...

  5. Python 从入门到实践(第二版) 第九章 练习9-15 彩票分析

    刚学到第九章,昨晚习题9-15的彩票分析,产生一个想法,但苦于不知如何开头,求助大神指点(下面是思路,见笑了~): '''编写一个自动根据数字范围进行对比测试选出号码的代码, 其中包括: 七星彩(Se ...

  6. 【Vue.js 牛刀小试】:第九章 - 组件基础再探(data、props)

    系列目录地址 一.基础知识概览 第一章 - 一些基础概念(posted at 2018-10-31) 第二章 - 常见的指令的使用(posted at 2018-11-01) 第三章 - 事件修饰符的 ...

  7. 工程项目管理丁士昭第二版_2021年软考系统集成项目管理工程师知识点预习第十四章第二节...

    听说99%的同学都来这里充电吖 为了方便大家尽早投入2021年的软考考试备考中,我们已开始连载<系统集成项目管理工程师>知识点,今天带来的是 第十四章 第二节 编制询价~ 知识点:第十四章 ...

  8. perl5 第九章 关联数组/哈希表

    第九章 关联数组/哈希表 by flamephoenix 一.数组变量的限制 二.定义 三.访问关联数组的元素 四.增加元素 五.创建关联数组 六.从数组变量复制到关联数组 七.元素的增删 八.列出数 ...

  9. DIP第九章习题解答

    数字图像处理 第九章课后作业 文档地址:数字图像处理第九章课后作业.pdf_数字图像处理第九章课后答案,数字图像处理第三版第九章课后答案-图像处理文档类资源-CSDN下载https://downloa ...

最新文章

  1. Microsoft training Kits
  2. 数据集的使用方法和技巧
  3. 树莓派查看mysql的密码_树莓派安装MySQL 后若何获取登录密码
  4. python 作物识别_Python-OpenCV —— 物体识别(TrainCascadeClassification)
  5. 网络安全-防火墙与入侵检测系统
  6. C++ setprecision()用法
  7. 计算机软件考试大纲,求计算机软件资格考试大纲和复习资料
  8. 华硕win10键盘失灵_Win10笔记本键盘失灵怎么办 Win10键盘失灵解决方法【详解】...
  9. 练习时长两年半的Matlab
  10. QT 以资源管理器打开文件夹
  11. fiddler视频分析
  12. 怎么批量在文件名前面加上数字序号,对文件进行编号排序?
  13. yolov4中的mosaic数据增强
  14. python与会计学_会计有必要学python吗?
  15. Centi和HandCash共同开发支付握手协议
  16. Datawhale组队学习21期_学术前沿趋势分析Task2_论文作者统计
  17. java的时间日期类_Java基础学习:日期时间类
  18. 华为电脑Linux进pe,华为 matebook X Pro用U盘PE重装系统步骤(xp)
  19. 【学以致用】JavaScript
  20. Springboot—mysql+mybatis+generator插件

热门文章

  1. ICCV2021 Oral-新任务!新数据集!康奈尔大学提出了类似VG但又不是VG的PVG任务
  2. CVPR 2021 接收论文临时列表!27%接受率!
  3. Nature、Science、Cell全加入!80家学术机构新冠研究全部免费
  4. 冠军方案 | 第二届中国“高分杯”美丽乡村大赛第一名总结
  5. EDG夺冠,我用Python分析一波:粉丝都炸锅了
  6. 收藏 | Pytorch-lightning的使用
  7. MIT探索深度学习网络的基础理论
  8. MLSQL解决了什么问题
  9. 3123称重显示控制器说明书_失重秤在自动化配料系统中的应用 - 工业自动化称重仪表...
  10. python深拷贝_python 深拷贝与浅拷贝的区别