弱事件模式

在应用程序中,为了与将处理程序附加到事件源的侦听器对象配合,附加到该事件源的处理程序可能不会被破坏。 这种情况可能会导致内存泄漏。 Windows Presentation Foundation (WPF) 引入了可用于解决此问题的设计模式,该模式为特定事件提供专用管理器类并为该事件的侦听器实现接口。 此设计模式称为“弱事件模式”。

为什么实现弱事件模式?

侦听事件可能会导致内存泄漏。 侦听事件的一般方法是使用语言特定的语法,该语法将处理程序附加到源上的事件。 例如,在 C# 中,该语法是:source.SomeEvent += new SomeEventHandler(MyEventHandler)。

此方法创建从事件源到事件侦听器的强引用。 通常,为侦听器附加事件处理程序会导致侦听器具有对象生存期,该生存期受源的对象生存期影响(除非显式移除了事件处理程序)。 但在某些情况下,您可能希望侦听器的对象生存期受其他因素(例如对象生存期当前是否属于应用程序的可视化树)控制,而不受源的生存期控制。 如果源对象生存期超出了侦听器的对象生存期,则常规事件模式会导致内存泄漏:侦听器保持活动状态的时间比预期要长。

弱事件模式旨在解决此内存泄漏问题。 每当侦听器需要注册事件,而该侦听器并不明确了解什么时候注销时,就可以使用弱事件模式。 当源的对象生存期超出侦听器的有用对象生存期时,也可以使用弱事件模式。 (在这种情况下,有用由您确定。)使用弱事件模式,侦听器可以注册和接收事件,同时不会对侦听器的对象生存期特征产生任何影响。 实际上,来自源的隐含引用无法确定侦听器是否适合进行垃圾回收。 该引用为弱引用,仅是弱事件模式和相关 APIs 的命名。 可以对侦听器进行垃圾回收或销毁,在不保留对现在销毁的对象的非可收集处理程序引用的情况下,源可以继续。

谁应实现弱事件模式?

实现弱事件模式主要是对于控件作者而言是很有趣味的。 这是因为控件作者主要负责控件的行为和包容以及控件对其所插入到的应用程序的影响。 这包括控件对象生存期行为,特别是对所描述的内存泄漏问题的处理。

某些方案本质上适合应用弱事件模式。 数据绑定就是这样一个方案。 在数据绑定中,通常源对象完全独立于侦听器对象,而该侦听器对象是绑定的目标。 WPF 数据绑定的许多方面在实现事件的方式上已应用了弱事件模式。

如何实现弱事件模式

实现弱事件模式由以下三个方面组成:

  • WeakEventManager 类派生一个管理器。

  • 在任何想要注册弱事件的侦听器的类上实现 IWeakEventListener 接口,而不生成源的强引用。

  • 注册侦听器时,对于想要侦听器使用该模式的事件,不要使用该事件的常规的 add 和 remove 访问器, 而是使用该事件的专用 WeakEventManager 中的 AddListener 和 RemoveListener 实现。

WeakEventManager

若要实现弱事件模式,通常需要按照与该事件 1:1 的关系创建一个管理器类。 例如,如果有一个名为 Spin 的事件,则将创建一个 SpinEventManager 类作为该事件的专用弱事件管理器。 如果该事件在多个源类中存在,并且在每一个类中的行为大体相同并共享相同的事件数据类型,则对于每一个事件都可以使用相同的管理器。

当从 WeakEventManager 类派生时,会重写两个虚方法并且公开名称不由虚拟模板专门控制但确实存在的多个其他成员。 重写用于通过 WPF 基础结构启动或终止事件传递模式。 其他成员提供功能,以便您自己的 IWeakEventListener 实现可以使用 WeakEventManager 将侦听器附加到该事件。

有关从 WeakEventManager 派生的更多信息,请参见 WeakEventManager 参考主题中的“对继承者的说明”一节。

IWeakEventListener

IWeakEventListener 接口具有单一的名为 ReceiveWeakEvent 的接口方法。 ReceiveWeakEvent 实现必须是集中实现,即将该类上存在的任何事件引用定向到相应的 WeakEventManager

有关实现 IWeakEventListener 接口的更多信息,请参见 ReceiveWeakEvent 方法引用主题中的“对实现者的说明”一节。

附加侦听器

假设有一个作为常规事件的 ClockwiseSpin 事件(由 Spinner 类型定义)。 如果有一个要作为侦听器的 SpinListener 侦听器类,则附加处理程序的常规方法(不使用弱事件模式)是使用 += 运算符:

spinnerInstance.ClockwiseSpin += new EventHandler(MyOnCWSpinHandler);

如果有一个实现 IWeakEventListener 并在该实现中说明 ClockwiseSpin 事件及其管理器的类,则使用弱事件模式的语法为:

ClockwiseSpinEventManager.AddListener(spinnerInstance, this);

在类的 ReceiveWeakEvent 实现的一种情况中,指定该事件的处理逻辑,而不是将该处理逻辑指定为基于委托的常规处理程序。

对外部事件实现该模式

弱事件模式的一个有意义的方面是,可以针对不属于基本代码一部分的事件实现该模式。 从源的角度来看,将处理程序附加到其事件的方法并没有什么不同之处,该方法受 WeakEventManager 的控制。 仅需要定义该事件的 WeakEventManager,然后在想要使用弱事件模式侦听该事件的任何预期侦听器上,将该事件说明为 ReceiveWeakEvent 逻辑的一部分。

请参见

参考

WeakEventManager

IWeakEventListener

概念

路由事件概述

数据绑定概述