在C#中使用Finalizers的好例子

16

在阅读一些有关C#内存管理的文章时,我被终结器方法所困惑。

与它们相关的规则非常复杂。例如,没有人知道终结器何时会被调用,即使在构造函数中的代码引发异常,它们也会被调用;CLR不保证在程序关闭时调用所有终结器等。

那么终结器可以在现实生活中用来干什么呢?

我找到的唯一一个例子就是当GC启动时哔哔声的程序。

您是否在代码中使用终结器,并且有一些好的示例?

更新:

当开发人员想要通过IDisposable确保某个类始终正确清除时,可以使用终结器。 (链接 ;感谢Steve Townsend)


4
在我过去五年参与的项目中,我只编写了很少几个 finalizer,并且它们都是为了与旧的非托管 API 进行交互的类而编写的。在正常操作下,因为对象在成为垃圾收集的候选项之前就被处理掉了,并且通过显式地禁止终结,所以这些 finalizer 实际上从未被调用过。 - Dan Bryant
@Dan Bryant - 是的 - 在回答之前没有看到这一点... - Steve Townsend
1
我一直认为finalizers的作用是在IDisposable.Dispose()未被调用(某些人忘记使用using()或finally)时,作为一个保险来处理未受管控的资源。只要您的Dispose() 调用SuppressFinalize,它们不会影响性能问题。 - Jimmy Hoffa
因此,在不必持有某些未管理的资源一段时间的情况下,我们可以使用 finalizers(终结器)以防程序员忘记调用 Dispose() 方法。而在其他情况下,当某些资源必须在确定的时间内释放时,我们应该使用 finalizers 来调试对象是否被正确释放。我做出了正确的总结吗? - mt_serg
2个回答

6
这里有一个详尽的讨论关于Finalizer的使用,附有例子,请参考此处。链接由@SLaks在相关答案中提供。
此外,也可以参考此处得到更简洁的总结,即当您需要时(这并不经常)。
还有一个很好的先前回答,其中包含另一个很好的现实世界的例子,请参考此处
简而言之,引用一段相关摘录:

Finalizers是必要的,以确保稀缺资源如文件句柄、套接字、内核对象等被释放回操作系统。

要了解更多正确的实际应用示例,请浏览.Net中相关类: https://learn.microsoft.com/en-us/search/?terms=.Finalize&scope=.NET 我能想到的一个合理的原因是,如果您在受控包装器中包装了第三方本地代码API,并且底层本地代码API库需要及时释放使用过的操作系统资源,则可能需要使用finalizer。

5
我所知道的最佳实践是简单明了地不使用它们。然而,在某些角落案例中,当处理非托管对象且无法实现Dispose模式(我不知道遗留问题)时,您可能需要使用终结器,然后可以小心地实现Finalize方法(并且它可能会降低系统性能,使您的对象成为未死亡状态和其他可能的奇怪场景,要注意异常,因为它们是无法捕获的:))。
在99%的情况下,只需编写使用Dispose模式并使用此方法清理自己的代码,并一切都将很好。

我不太理解你在这里的意思(你可能需要稍微正式一些),但我百分之百确定,在99%的情况下,你不想接近finaliser。 - annakata
这正是我所说的。如果不清楚,我可能会编辑答案。Finalizers非常危险,在实现它们时需要非常小心。正如之前的人所说,只有在处理某些未托管资源时才需要它们。但是,与遗留技术进行接口或使用一些C/C++库可能会批准使用它。 - luckyluke
一个类几乎不应该有终结器,除非这个类的目的围绕着它拥有一个终结器(例如 SafeHandle)。如果一个复杂的对象将要独占某个将会比它存在更长时间的东西,那么它应该创建另一个唯一目的是管理该物品的对象。如果一个带有终结器的对象持有对其他对象的引用,那么可能会出现许多复杂情况,这些对象反过来又持有对其他对象的引用等等。具有讽刺意味的是,Dispose 模式提供了显式支持,使得派生类可以添加终结器 - 我怀疑这从来都不是一个好主意。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接