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

时间:2012-06-01 08:57:58

标签: c# .net destructor idisposable

我有一个实现IDisposable的C#类,在Dispose()实现中,我在同样实现Dispose()的子项目上调用了IDisposable

同一类的析构函数怎么样?我必须在那里做一些特别的事吗?

3 个答案:

答案 0 :(得分:1)

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

请注意,“析构函数”或“终结器”会加速对象的破坏 - 相反,它会为将被销毁的对象提供缓存以运行其Finalize()方法(对于C#程序,它将依次运行析构函数中的代码)。如果具有终结器的对象知道(1)需要在宇宙结束之前发生的事情,(2)没有其他对象要做,并且(3)可以在任意的内部安全地完成,这可能是有用的。未知的线程上下文。请注意,终结器很少用于在其他对象上调用IDisposable.Dispose。如果这些对象可以处理从任意线程上下文中处理的问题,那么它们可能最终自己完成(因此它们的处理不符合要求#2);如果他们无法处理从任意线程上下文处置的问题,则不能将其置于终结器内(要求#3)。

顺便说一句,微软早期开发的.net似乎认为实现IDisposable但没有终结器的类应该做出规定,这样派生类可以添加终结器,并且他们继续推荐{{1允许这种情况的模式。虽然派生类有时会有一个“警钟”终结器,如果在一个尚未处理的对象上调用它会产生某种警告,有时候我会建议一个派生于非平凡的类没有终结器的类不应该尝试在finalize方法或析构函数中执行清理。最终确定涉及一些棘手的极端情况,如果在继承链的每一步处理不当,可能会导致Heisenbugs(不可预测的失败)。如果基类不是为支持可靠的最终化清理而设计的,那么向派生类添加终结器可能会破坏原本可行的代码。

答案 1 :(得分:0)

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

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

更新

看起来像是有人在他们的评论中发布了一个构建析构函数的理由:)

你每天都在这里学到新东西:D

答案 2 :(得分:0)

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

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

如果你确实需要终结者,那么你应该在你的处置方法中调用GC.SuppressFinanlize。来自implementing a Dispose method的文档:

  

Dispose方法应该调用SuppressFinalize方法   对象它正在处置。如果对象当前正在完成   queue,SuppressFinalize阻止其Finalize方法   调用。请记住,执行Finalize方法的成本很高   性能。如果您的Dispose方法已经完成了清理工作   对象,然后垃圾收集器没有必要   调用对象的Finalize方法。

请参阅GC.SuppressFinanlize documentation了解包含终结器的示例模式。