终结器很少有用。您链接到的文档并不完全有帮助 - 它提供了以下相当循环的建议:
仅对需要终结的对象实施终结
这是提出问题的一个很好的例子,但它不是很有帮助。
在实践中,绝大多数时候你不需要终结器。(.NET 开发人员必须经历的学习曲线之一是发现在他们认为需要终结器的大多数地方,他们不需要。)您已将其标记为(除其他外)一个 WPF 问题,并且我会说在 UI 对象上放置终结器几乎总是一个错误。(因此,即使您处于需要终结器的异常情况之一,该工作也不属于与 WPF 相关的代码附近的任何地方。)
在大多数情况下,终结器看起来可能有用,但事实证明它们并非如此,因为当你的终结器运行时,它已经为时已晚,无法做任何有用的事情。
例如,尝试对对象引用的任何对象执行任何操作通常是一个坏主意,因为在终结器运行时,这些对象可能已经终结。(.NET 不保证终结器的运行顺序,因此您根本无法知道您引用的对象是否已经终结。)在终结器已经完成的对象上调用方法是个坏主意被运行。
如果您有某种方式知道某个对象肯定还没有被终结,那么使用它是安全的,但这是一个非常不寻常的情况。(......除非有问题的对象没有终结器,并且本身不使用可终结的资源。但在这种情况下,当你自己的对象消失时,它可能不是你实际上需要做任何事情的对象。)
终结器似乎有用的主要情况是互操作:例如,假设您正在使用 P/Invoke 调用一些非托管 API,并且该 API 会返回一个句柄。也许您需要调用其他一些 API 来关闭该句柄。由于这都是非托管的东西,.NET GC 不知道这些句柄是什么,你的工作是确保它们被清理,此时终结器是合理的......除了在实践中,它几乎总是最好的用于SafeHandle
该场景。
在实践中,我发现自己使用终结器的唯一地方是 a) 旨在调查 GC 功能的实验,以及 b) 旨在发现特定对象在系统中如何使用的诊断代码。两种代码都不应该最终投入生产。
所以你是否需要“在子类中实现终结器”的答案是:如果你需要问,那么答案是否定的。
至于是否复制标志......其他答案在这里提供了相互矛盾的建议。要点是 1)您确实需要调用 baseDispose
和 2)您Dispose
需要是幂等的。(即,它是否被调用一次、两次、5 次、100 次都没有关系——如果它被多次调用,它不应该抱怨。)你可以随意实现它——布尔标志是一种方法,但我经常发现null
在我的Dispose
方法中设置某些字段就足够了,此时不需要单独的布尔标志 - 你可以说它Dispose
已经被调用,因为你已经将这些字段设置为null
.
那里的许多指导IDisposable
都非常无用,因为它解决了您需要终结器的情况,但这实际上是一个非常不寻常的情况。这意味着很多人编写的IDisposable
实现比必要的复杂得多。在实践中,大多数类在jpierson 链接到的文章中都属于 Stephen Cleary 所称的“级别 1”类别。对于这些,您不需要所有使大多数示例混乱的GC.KeepAlive
、GC.SuppressFinalize
和东西。Dispose(bool)
大多数时候,生活实际上要简单得多,正如克利里对这些“1 级”类型的建议所表明的那样。