【问题标题】:Can delegates prevent an object from being garbage collected?委托可以防止对象被垃圾收集吗?
【发布时间】:2016-05-27 08:17:50
【问题描述】:

假设我有一个类定义为:

class MyWrapper<T> where T : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  private void OnPropertyChange(String propname)
  {
    handlePropertyChanged(this, new PropertyChangedEventArgs(propname));
  }

  private void handlePropertyChanged(object sender, PropertyChangedEventArgs e)
  {
      var handler = PropertyChanged;
      if (handler != null)
      {
          handler(sender, e);
      }
  }

  private T _backing;
  public MyWrapper(T backing)
  {
    _backing=backing;
    _backing.PropertyChanged+=handlePropertyChanged;
  }

  ...Reasons for wrapping the class...
}

此类在支持对象的 PropertyChanged 事件上设置委托。 据我了解,这个委托有一个指向 MyWrapper 实例的指针。这是否意味着 MyWrapper 实例的生命周期不会短于烘焙对象?

如果是这样,有没有办法解决这个问题?

【问题讨论】:

    标签: c# garbage-collection


    【解决方案1】:

    如果您真的想保留引用而不阻止 GC,请查看弱引用。这些允许您保留对对象的引用,只要它存在(您始终需要明确检查对象是否仍然存在并提取“真实”的强引用)。

    在代表的情况下,WeakEvent 是合适的。

    不过,我也建议另一种方法。如果事件处理程序真的没有做任何事情(如在您的示例代码中),您可以完全跳过它,并像这样声明PropertyChanged

    public event PropertyChanged
    {
        add { _backing.PropertyChanged += value; }
        remove { _backing.PropertyChanged -= value; }
    }
    

    或者,如果处理程序很重要(例如,它转换输入等),您可以考虑使用显式处置模式来取消注册任何事件处理程序:

    public void Dispose() { _backing.PropertyChanged -= handlePropertyChanged; }
    

    使用完包装器对象后,只需调用Dispose(或者,如果有固定范围,请使用using)。这是简化的 - 正确的处置模式有点复杂,但如果您不处理非托管资源,则不是真正必要的。

    【讨论】:

    • 应该是我要收集的MyWrapper对象。除了代表,我没有看到任何提及。
    • @Luuan 您可能想要更新答案 - WeakEvent 和/或处置模式可能是正确的答案。上面的代码做了一些事情;它将程序中对支持对象进行更改的部分(并且不了解包装对象)与通过包装器访问的对象(并且不了解实际的包装对象)联系在一起。
    • WeakEvent 显然是要走的路。特别是因为 PropertyChanged 的​​ Manager 已经准备就绪。
    • @Taemyr 更新:)
    • 我不知道您可以对事件进行自定义添加/删除。这很整洁。
    【解决方案2】:

    是的,另一个属性持有的对事件句柄的引用会阻止 MyWrapper 类实例被垃圾回收。只有当对处理程序的所有引用都被释放时(例如,当这些实例也符合 GC 条件时)才能从内存中释放 MyWrapper 实例。

    这个问题和之前发的一个类似:Do event handlers stop garbage collection from occuring?

    编辑: 要回答您对代理的评论,请将handlePropertyChanged 作为代理分配给backing,这会创建对MyWrapper 的外部引用。因此,MyWrapper 在删除该引用之前不会被 GC。

    有几种方法可以解决这个问题:

    1. 使用弱引用:https://msdn.microsoft.com/en-us/library/system.weakreference(v=vs.110).aspx
    2. 一旦不再需要对委托/事件的引用,请立即删除。

    【讨论】:

    • 我想你误解了我的问题。我在问我在烘焙场上设置的委托是否会阻止 MyWrapper 对象被释放。我不太关心引用 MyWrapper 实例的事件句柄的对象会阻止包装器被释放。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多