4

在这里阅读答案后,我决定将我的类标记为密封,以简化IDisposable实现。为什么sealed会影响IDisposable的实现(例如GC.SuppressFinalize(this);不需要调用)?请解释发生了什么。我需要能够向其他开发人员解释为什么我将课程密封起来。

4

3 回答 3

2

如果实现的类IDisposable不是密封的,则派生类很可能需要做一些事情来响应,但也应该执行Dispose基类的操作。Dispose如果该类公开一个Dispose始终与 同义的公共成员,则IDisposable.Dispose可以在 C# 中通过简单地使用带有公共虚拟Dispose方法的隐式接口实现来实现必要的语义。

这种方法有两个问题:

  1. 在父类公开一个公共的“Dispose”方法的情况下,它需要派生类使用不同的方法,而不是在它不公开的情况下;如果一个不公开公共“Dispose”方法的类被一个公开的类继承,事情可能会变得非常混乱。
  2. 一些基本处理代码应该在派生类处理代码之前运行(例如,抑制重复“处理”尝试的代码),而一些应该在之后运行(例如,“GC.SuppressFinalize()”)。实现这一点的唯一方法是让非虚拟包装器调用受保护的虚拟函数。请注意,顺便说一句,Microsoft 的包装器没有正确抑制重复的-`Dispose`,但包装器是此类抑制代码的唯一好地方。

请注意,Microsoft 似乎打算将其Dispose模式用于基类不覆盖Finalize但派生类Finalize用于清理的情况。虽然这可能是本意,但这并不是一个好的模式。除了极少数例外,唯一应该重写Finalize以进行清理的类是那些派生自诸如Object. 如果一个类实现IDisposable但不覆盖Finalize,则派生类应该覆盖的唯一目的Finalize是在被调用时发出警报Finalize,甚至这种用法也值得商榷(更好的模式是:

类什么:IDisposable
{
  IDisposable DisposedStatusObject;
  // 生成一个静态虚拟对象实例,我们可以将其用作哨兵值
  // 它必须是 `IDisposable`,但实际上不应持有任何资源。

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

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

  任何()
  {
    DisposedStatusObject = new DisposalAlarm(); // 构造函数中的第一件事
  }
  无效处置()
  {
    IDisposable prevStatus;
    prevStatus = Interlocked.Exchange(DisposedStatus, DisposedStatusDisposed);
    if (prevStatus != DisposedStatusDisposed)
    {
      处置(真);
      prevStatus.Dispose();
    }
  }
}

该类DisposalAlarm()被假定为具有覆盖方法的类,如果该方法在没有首先调用其方法的情况下被调用Finalize(),则会发出警报。的方法将确保,如果派生类方法正确返回,警报将被取消。请注意,如果 的实例具有未抑制的终结器,则必须保留直接或间接引用的所有内容,直到该终结器运行或被抑制为止。相比之下,添加一个对象并不会延长.Finalize()Dispose()DisposewhateverwhateverwhateverDisposalAlarmwhatever

于 2012-06-13T23:30:52.793 回答
1

创建一个类sealed意味着不能有从它派生的类。这意味着实现IDisposable不需要考虑派生类的行为(或不当行为)。

于 2012-06-13T22:38:13.407 回答
1

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

于 2012-06-13T22:39:28.327 回答