在这里阅读答案后,我决定将我的类标记为密封,以简化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()
Dispose
whatever
whatever
whatever
DisposalAlarm
whatever
创建一个类sealed
意味着不能有从它派生的类。这意味着实现IDisposable
不需要考虑派生类的行为(或不当行为)。
密封类不打算用作基类,而非密封类是。因此存在区别:非密封类需要为其派生类提供一种实现Dispose()
自己的方法,而密封类则没有这种责任,因为它无法扩展。