1

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

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

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

否则,即使事件处理程序的对象之一被分配为 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<object> 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”方法(实际上我实现了一个),“选项(2)WeakReference”将与“选项(1)显式分离事件”没有区别。(Mediator 本身仍然是一个有用的模式,可以穿透 WPF 或 MVVM 组件层的层次结构)

4

2 回答 2

5

如果我明白你在问什么,那么有必要澄清一下。

否则,即使事件处理程序的对象之一被分配为 null,“孤儿”对象仍将响应该事件 - 这将导致意外行为。

并不真地。这不是意外的行为。如果您不明确取消注册,则完全期望调用该对象。

弱事件的整个想法是一个安全网,它不会仅仅因为它们订阅了一个事件而将对象保留在内存中。当对象超出范围时,它与从事件中注销对象无关。

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

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

弱事件与对象超出范围时的自动取消订阅无关。

于 2011-07-11T16:32:47.110 回答
1

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

于 2011-07-29T21:50:10.930 回答