C#语言:为什么WeakReference或弱事件模式?

我正在阅读“C#语言”,第4版,它谈到WeakReferenceWeak Event Pattern

CHRISTIAN NAGEL:内存泄漏通常是由于错误使用事件造成的。 如果客户端对象附加到事件但不从它们分离,并且不再使用对客户端对象的引用,则垃圾回收器仍然无法回收客户端对象,因为发布者的引用仍然存在。 这可以通过(1)在不再使用客户端对象时分离事件,(2)使用持有委托的WeakReference类的自定义实现addremove访问器,或(3) Weak Event pattern避免。 WPF使用IWeakEventListener接口。

我在这里有疑问:选项“(2) WeakReference ”根本没有带来任何便利 ,比较“选项(1)明确地分离事件”,因为使用WeakReference仍然需要显式调用addremove

否则,即使其中一个事件处理程序的对象被指定为null,“孤立”对象仍将响应该事件 – 这将导致意外行为。

注意: WeakReference仅以事件处理程序对象不会受事件发布者对象影响的方式帮助垃圾收集; WeakReference不会强制事件处理程序对象被垃圾收集。

类似的问题也适用于弱事件模式。

也许这有点抽象,以Josh Smith的Mediator模式(http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/)为例。

 public class Mediator //... { public void Register(object message, Action callback) { // notice: Mediator has no Unregister method } public void NotifyColleagues(object message, object parameter) { // ... } } public class ObjectA //... { public string ObjectAText { get { return _objectAText; } set { //... _mediator.NotifyColleagues(MediatorMessages.ObjectASaidSomething, _objectAText); } } } public class ObjectB //... { //... public ObjectB(Mediator mediator) { //... _mediator.Register( MediatorMessages.ObjectASaidSomething, param => { // handling event ObjectASaidSomething }); } } 

如果我们有

 ObjectA objectA = new ObjectA(); ObjectB objectB1st = new objectB(); objectA.ObjectAText = "John"; // objectB1st will respond to this event. objectB1st = null; // due to delay of garbage collection, the object is actually still in memory ObjectB objectB2nd = new objectB(); objectA.ObjectAText = "Jane"; // both objectB1st and objectB2nd will respond to this event! 

由于WeakReference ,最后一行不会导致意外行为吗?

但是如果Mediator类提供“Unregister”方法(实际上我实现了一个),“option(2) WeakReference ”与“option(1)明确地分离事件”没有区别。 (Mediator本身仍然是一个有用的模式,可以穿透WPF或MVVM组件层的层次结构)

如果我理解你的要求,那就需要做一些澄清。

否则,即使其中一个事件处理程序的对象被指定为null,“孤立”对象仍将响应该事件 – 这将导致意外行为。

并不是的。 这不是意料之外的行为。 如果您没有显式取消注册,则完全期望调用该对象。

弱事件的整个想法是一个安全网,因为他们订阅了一个事件,所以不能将对象保留在内存中。 它与超出范围的事件中的对象取消注册无关。

如果您需要执行稍后操作,请为订阅者使用IDisposable模式和“using”构造,或者显式取消订阅。

即弱事件是一个非常具体的问题的解决方案 – 允许垃圾收集对象,这些对象订阅了一个长生命对象(如GUI或某些静态类)。

即使在对象超出范围的那一刻,弱事件也不是关于自动取消对象。

如果事件订阅者和发布者都合作,则可以在.net中实现合理的弱事件模式,而无需reflection或其他CLR技巧。 如果事件的取消订阅方法在终结器线程调用时需要正确运行,则事件订阅者可以单方面实现弱事件模式,但不幸的是,当订阅来自未知类的事件时,这种期望是不合理的(例如一个INotifyPropertyChanged)。 对于那些真正“感兴趣”某个对象的人来说,诀窍就是拥有对包装器的强引用,以及事件处理程序和其他东西来保持对对象“胆量”的引用。 包装器可以使用Finalize方法保存对内部和对象的引用,该方法将取消订阅事件。