在这里阅读答案后,我决定将我的类标记为密封,以简化IDisposable实现。为什么sealed会影响IDisposable的实现(例如GC.SuppressFinalize(this);不需要调用)?请解释发生了什么。我需要能够向其他开发人员解释为什么我将课程密封起来。
3 回答
如果实现的类IDisposable不是密封的,则派生类很可能需要做一些事情来响应,但也应该执行Dispose基类的操作。Dispose如果该类公开一个Dispose始终与 同义的公共成员,则IDisposable.Dispose可以在 C# 中通过简单地使用带有公共虚拟Dispose方法的隐式接口实现来实现必要的语义。
这种方法有两个问题:
- 在父类公开一个公共的“Dispose”方法的情况下,它需要派生类使用不同的方法,而不是在它不公开的情况下;如果一个不公开公共“Dispose”方法的类被一个公开的类继承,事情可能会变得非常混乱。
- 一些基本处理代码应该在派生类处理代码之前运行(例如,抑制重复“处理”尝试的代码),而一些应该在之后运行(例如,“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
创建一个类sealed意味着不能有从它派生的类。这意味着实现IDisposable不需要考虑派生类的行为(或不当行为)。
密封类不打算用作基类,而非密封类是。因此存在区别:非密封类需要为其派生类提供一种实现Dispose()自己的方法,而密封类则没有这种责任,因为它无法扩展。