WeakEvent 模式

更新:2007 年 11 月

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

为什么实现 WeakEvent 模式?

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

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

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

谁应实现 WeakEvent 模式?

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

某些方案本质上是将自己借给 WeakEvent 模式的应用程序。数据绑定就是这样的方案之一,在数据绑定中,作为数据源的源对象通常完全独立于作为绑定目标的侦听器对象。WPF 数据绑定的许多方面在实施事件的方式上已应用了 WeakEvent 模式。

如何实现 WeakEvent 模式?

实现 WeakEvent 模式由三个方面组成:

  • WeakEventManager 类派生一个管理器。

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

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

WeakEventManager

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

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

有关从 WeakEventManager 派生的详细实现说明,请参见 WeakEventManager 参考主题中的“对继承者的说明”。

IWeakEventListener

IWeakEventListener 实现类仅有一种责任:实现接口方法 ReceiveWeakEventReceiveWeakEvent 实现必须是集中实现,即将该类上存在的任何事件引用定向到相应的 WeakEventManager

有关实现 IWeakEventListener 接口的详细实现说明,请参见 ReceiveWeakEvent 方法引用主题中的“对实现者的说明”。

附加侦听器

假设有一个作为常规事件的 ClockwiseSpin 事件(由 Spinner 定义)。若要对此事件使用该模式,可使用从 WeakEventManager 派生的现有 ClockwiseSpinEventManager 类或亲自实现该事件。如果有一个要作为侦听器的 SpinListener 侦听器类,则附加处理程序的常规方法(不使用模式)是使用 += 语法:

spinnerInstance.ClockwiseSpin += new EventHandler(MyOnCWSpinHandler);

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

ClockwiseSpinEventManager.AddListener(spinnerInstance, this);

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

对外部事件实现该模式

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

请参见

概念

路由事件概述

数据绑定概述

参考

WeakEventManager

IWeakEventListener