c#语言:为什么是弱引用或弱事件模式

本文关键字:引用 事件 模式 语言 为什么 | 更新日期: 2023-09-27 18:01:50

我在读《The c# Language》第4版,里面讲的是WeakReferenceWeak Event Pattern:

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

我在这里有疑问:选项"(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<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组件层的层次结构)

c#语言:为什么是弱引用或弱事件模式

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

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

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

弱事件的整个思想是一个安全网,避免仅仅因为对象订阅了事件而将它们保存在内存中。当对象超出作用域时,它与从事件中注销对象无关。

如果您需要执行后一种操作,可以为订阅者使用IDisposable模式和"using"构造,或者显式取消订阅。

。弱事件是一个非常特殊的问题的解决方案——允许对象的垃圾收集,这些对象被订阅到一个长期存活的对象(如GUI或一些静态类)。

弱事件不是在对象超出作用域时自动从偶数中取消。

如果事件订阅者和发布者都合作,则可以在。net中实现合理的弱事件模式,而无需反射或其他CLR技巧。如果事件的取消订阅方法需要在结束器线程调用时正确工作,则事件订阅者可能单方面实现弱事件模式,但不幸的是,当从未知类(例如INotifyPropertyChanged)订阅事件时,这种期望是不合理的。诀窍在于,任何真正对一个对象"感兴趣"的人都可以持有对包装器的强引用,而事件处理程序和其他东西可以持有对对象"内部"的引用。包装器可以同时保存对内脏和具有Finalize方法的对象的引用,Finalize方法可以取消订阅事件。