为什么'密封'会影响IDisposable的实现?

时间:2012-06-13 22:36:23

标签: .net .net-2.0 idisposable sealed

在阅读答案here后,我决定将我的课程标记为已密封,以简化IDisposable实施。密封为什么会影响IDisposable的实现(例如GC.SuppressFinalize(this);不需要调用)?请解释一下发生了什么。我需要能够向同事解释为什么我把课程密封了。

3 个答案:

答案 0 :(得分:2)

如果实现IDisposable的类没有被密封,则派生类可能需要做一些事情来响应Dispose,但是Dispose的基类操作也应该进行。如果类公开了一个永远与Dispose同义的公共IDisposable.Dispose成员,那么只需使用带有公共虚拟Dispose方法的隐式接口实现,就可以在C#中实现必要的语义。 / p>

这种方法存在两个问题:

  1. 在父公开`Dispose`方法的情况下,它会要求派生类使用不同的方法,而不是在公开`Dispose`方法的情况下。如果没有暴露公共`Dispose`方法的类被未密封的类继承,那么事情可能变得非常混乱。
  2. 一些基本处理代码应该在派生类处理代码之前运行(例如,抑制重复`Dispose`尝试的代码),有些应该在之后运行(例如`GC.SuppressFinalize()`)。实现这一目标的唯一方法是让非虚拟包装器调用受保护的虚拟功能。注意,顺便说一句,微软的包装器没有正确地抑制重复的`'Dispose`,但包装器是这种抑制代码的唯一好处。

请注意,Microsoft似乎打算在基类不覆盖Dispose的情况下使用其Finalize模式,但派生类使用Finalize进行清理。虽然这可能是意图,但这并不是一个很好的模式。除了极少数例外,唯一应该仅覆盖Finalize进行清理的类是那些派生自像Object这样的普通类的类。如果类实现IDisposable但不覆盖Finalize,则派生类应覆盖Finalize的唯一目的是在Finalize被调用时发出警报,并且即使这种用法是有争议的(一个更好的模式将是:

class whatever:IDisposable
{
  IDisposable DisposedStatusObject;
  // Generate a static dummy object instance we can use as a sentinel value
  // It needs to be `IDisposable`, but shouldn't actually hold any resources.

  static IDisposable DisposedStatusDisposed = new List<int>().GetEnumerator();

  public bool Disposed {get {return (DisposedStatusObject == DisposedStatusDisposed);} }

  whatever()
  {
    DisposedStatusObject = new DisposalAlarm(); // First thing in constructor
  }
  void Dispose()
  {
    IDisposable prevStatus;
    prevStatus = Interlocked.Exchange(DisposedStatus, DisposedStatusDisposed);
    if (prevStatus != DisposedStatusDisposed)
    {
      Dispose(true);
      prevStatus.Dispose();
    }
  }
}

假定DisposalAlarm()类是一个具有重写Finalize()方法的类,如果在没有首先调用Finalize()方法的情况下调用Dispose()方法,则会发出警报。 Dispose的{​​{1}}方法将确保,如果派生类方法正确返回,则警报将被取消。请注意,如果whatever的实例具有未抑制的终结器,则whatever持有直接或间接引用的所有内容都必须保留,直到该终结器已运行或已被抑制。相比之下,添加whatever对象不会延长DisposalAlarm中任何内容的生命周期。

答案 1 :(得分:1)

创建类sealed意味着不能从中派生类。这意味着IDisposable的实现不需要考虑派生类的行为(或错误行为)。

答案 2 :(得分:1)

密封类不打算用作基类,而未密封的类则用作基类。因此存在区别:未密封的类需要为其派生类提供一种方法来实现它们自己的Dispose(),而密封类则没有这个责任,因为它无法扩展。