我是否必须在实现 IDisposable 的类的析构函数中执行任何特殊操作



我有一个实现IDisposable的 C# 类,在我的 Dispose() 实现中,我在也实现 IDisposable 的子弹射上调用Dispose()

同一类的析构函数呢?我必须在那里做什么特别的事情吗?

C# 析构函数语法指示编译器重写Object.Finalize() 。 覆盖Object.Finalize()的类被称为具有"终结器";所有这些对象都放置在一个名为"最终队列"的特殊列表中(术语"队列"可能有点奇怪,因为该列表没有语义相关的排序)并标记为"最终可完成"。 执行垃圾回收时,系统首先标记在终结队列之外存在直接或间接强引用的所有对象。 然后,系统会检查每个可完成的对象,以查看它是否已被标记。 如果没有,它将被标记为"不可完成",但将被添加到名为"可触及的[有效到达]队列"的队列中。 最后,系统会将可破坏队列中的所有对象标记为"活动",丢弃所有未标记的对象,并且 - 如果可破坏队列不为空 - 调度一个线程开始调用Finalize其中包含的所有项目。 请注意,位于"可破坏队列"中的对象,以及它们拥有强引用的每个对象,将被视为"活动",直到它们的Finalize()方法运行为止;由于它们将被标记为"不可完成",因此在此之后它们将有资格进行垃圾回收,除非它们被重新标记为"可完成",或者对它们的强烈引用已存储在活动对象中。

请注意,"析构函数"或"终结器"确实会加速对象的销毁 - 相反,它将为本应被销毁的对象提供缓刑以运行其Finalize()方法(对于 C# 程序,该方法又将运行析构函数中的代码)。 如果具有终结器的对象知道 (1) 需要在宇宙末日之前发生,(2) 没有其他对象会做,以及 (3) 可以在任意和未知的线程上下文中安全地完成,这可能很有用。 请注意,终结器很少用于调用其他对象上的IDisposable.Dispose。 如果这些对象可以处理从任意线程上下文中释放出来,它们可能会自行完成(因此它们的处置不符合要求 #2);如果它们无法处理从任意线程上下文中释放,则无法在终结器中释放它们(要求 #3)。

顺便说一下,Microsoft.net开发的早期似乎认为实现IDisposable但没有终结器的类应该做出规定,以便派生类可以添加终结器,并且他们继续推荐一种Dispose模式来考虑这一点。 虽然派生类有时有一个"警钟"终结器很有用,如果对尚未释放的对象调用它,它会生成某种警告,但我建议从没有终结器的非平凡类派生的类不应该尝试在 finalize 方法或析构函数中执行清理。 最终完成涉及一些棘手的极端情况,如果在继承链的每一步都没有完美处理,可能会导致Heisenbugs(不可预测的故障)。 如果基类不是为支持可靠的终结清理而设计的,则向派生类添加终结器可能会破坏本来可以工作的代码。

不,只要您释放 Dispose 实现中的任何资源就可以了。Microsoft建议使用 using() 语句实现 IDisposable 接口的使用,以便应用在对象超出范围时立即调用 dispose。听起来你做得很好 - 只要确保你在使用该实现时尝试使用 using()。

我知道有时这是不可能的,你希望范围持续更长的时间,但一次性通常用于处理非托管资源,所以你往往在这些事情上没有太多的一生

更新:

看起来也有理由创建一个析构函数,因为有人在他们的评论中发布了:)

在这里,您每天都能学到新东西:D

我想你的意思是终结器而不是析构函数

请注意,终结器非常不寻常。当然,每个一次性物品都应该有一个终结器,这当然不是真的。通常,只有控制非托管资源的对象(即在 CLR 控制之外获取的资源)才可能需要终结器。

如果您确实需要一个终结器,那么,您应该在 dispose 方法中调用 GC.SuppressFinanlize。来自实现Dispose方法的文档:

处置方法应调用 抑制终结方法 它正在处置的对象。如果对象当前正在定版中 队列,抑制完成阻止其 Finalize 方法被 叫。请记住,执行 Finalize 方法的成本很高 性能。如果您的处置方法已经完成了清理工作 向上对象,则垃圾回收器不必 调用对象的 Finalize 方法。

有关包含终结器的示例模式,请参阅GC.SuppressFinanlize文档。

最新更新