0

我有一个实现的 C# 类,IDisposable在我的Dispose()实现中我调用Dispose()了也实现IDisposable.

同一个类的析构函数呢?我需要在那里做一些特别的事情吗?

4

3 回答 3

1

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

请注意,“析构函数”或“终结器”确实会加速对象的销毁——相反,它会为已被销毁的对象提供暂缓运行其Finalize()方法的时间(对于 C# 程序,该方法将依次运行析构函数)。如果具有终结器的对象知道(1)需要在宇宙结束之前发生的事情,(2)没有其他对象会做,并且(3)可以在任意和未知的线程上下文。请注意,很少使用终结器来调用IDisposable.Dispose在其他物体上。如果此类对象可以处理从任意线程上下文中释放的问题,那么它们可能会自行完成(因此它们的释放不符合要求#2);如果它们无法处理从任意线程上下文中释放的问题,则它们不能在终结器中释放(要求 #3)。

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

于 2012-06-01T15:23:13.097 回答
0

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

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

更新:

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

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

于 2012-06-01T09:01:50.883 回答
0

我认为您的意思是终结器而不是析构器

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

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

Dispose 方法应为其正在处理的对象调用 SuppressFinalize 方法。如果对象当前在终结队列中,则 SuppressFinalize 会阻止调用其 Finalize 方法。请记住,执行 Finalize 方法对性能的影响很大。如果您的 Dispose 方法已经完成了清理对象的工作,那么垃圾收集器就没有必要调用对象的 Finalize 方法。

请参阅GC.SuppressFinanlize文档以获取包含终结器的示例模式。

于 2012-06-01T09:04:08.590 回答