10

我知道在 C# 中,如果您编写~MyClass(),这基本上会转换为override System.Object.Finalize(). 因此,无论您是否编写析构函数,CLR 中的每种类型都将有一个Finalize()方法(System.Object至少)。

1] 那么,这是否意味着,默认情况下,每个对象都有一个终结器?

2] CLR 决定一个对象应该进入终结队列的依据是什么?

我问这个,因为,我有一个类,说ManagedResourceHolder已实现IDisposable,但没有调用GC.SuppressFinalize(this)它的IDisposable.Dispose()方法。该类没有任何非托管资源,并且不需要该~ManagedResourceHolder()方法,这反过来意味着不需要 GC.SuppressFinalize(this)调用,因为没有终结器。

3] 在上述情况下,实现 IDisposable 时是否总是需要提供终结器?(即使在一个没有非托管资源的类上)

FxCop 规则CA1816违反了这一点,当我在 MSDN 上的 CA 论坛上提问时,我得到的回复让我感到困惑

谢谢。

4

4 回答 4

17

问题 1 和 2:CLR 基本上检查终结器是否被覆盖。如果不是,它会将其视为没有终结器。

在 System.Object 中有一个终结器的好处是编译器知道他们总是可以调用base.Finalize()in。这避免了版本控制问题。考虑一个没有 的世界System.Object.Finalize()

  • System.Object(没有完成)
  • Acme.BaseClass(没有 Finalize)
  • MyCompany.DerivedClass(完成)

如果对象中没有Finalize方法,MyCompany.DerivedClass 中的终结器就不能调用任何东西。当 Acme.BaseClass 的第 2 版带有终结器时,这会导致问题。除非您重新编译 MyCompany.DerivedClass,否则 DerivedClass 的实例将在不调用 BaseClass.Finalize 的情况下完成,这显然是一件坏事。

现在考虑System.Object.Finalize 相同的情况 - 编译器在 DerivedClass.Finalize 中自动插入对 base.Finalize 的调用,在版本 1 中它只是调用 System.Object 中的无操作实现。当 Acme.BaseClass 的第 2 版发布时,对base.Finalizewill 的调用(无需重新编译 DerivedClass)调用 BaseClass.Finalize。

问题 3:不,你不需要仅仅因为你实现了 IDisposable 就拥有一个终结器。终结器应该只用于非托管资源,没有其他东西可以清理- 即您可以直接引用的资源。例如,假设您有一个具有FileStream成员变量的类。如果调用者记得,您想要实现IDisposable以便可以尽快关闭流 - 但如果他们记得调用Dispose(),则流将与您的对象同时有资格进行垃圾收集。相信FileStream有一个适当的终结器(或使用终结器等引用其他东西),而不是试图在你自己的终结器中清理它。

从 .NET 2.0 开始,使用SafeHandle类,您需要自己的终结器应该是非常罕见的。

于 2008-12-11T08:56:03.537 回答
3

1:只有在被覆盖时才真正重要(在有用的意义上)

2:如 1 所定义,并且 GC.SuppressFinalize 没有被调用(加上重新注册等)

3:当然不是;事实上,除非您直接处理非托管资源,否则您不应该有终结器。您不应该仅仅因为它是 IDisposable 而添加终结器 - 但具有终结器的东西通常也应该是 IDisposable。

于 2008-12-11T09:00:26.180 回答
0
  1. 不,不是这个意思。只有被覆盖Finalize()的才会被 CLR 计算在内。
  2. 通过具有如上所述的终结器。
  3. 不,这并不总是必要的。这只是一个很好的模式。我的意思是,没有人强迫你这样做。但是,如果您有非托管资源,这是一件好事,因为如果有人忘记处置它,非托管资源将在某个时候被释放。FxCop 不执行严格的规则。如果您不注意,它会强制执行可能导致未来失败的良好模式。

更新:每个班级都负责管理自己的资源。由于面向对象范式的抽象和封装特性,一个类的使用者不应该间接关心它拥有什么资源。因此,你应该要么手动释放你拥有的资源(你拥有的就是你直接拥有的,你把其他东西看成一个黑盒子)或者把它留给GC来释放它们。对于非托管资源,您没有选择将其留给 GC,因此您必须手动释放它。从这个意义上说,Jon 提到的 SafeHandle 是非托管资源的托管抽象,因此它应该被视为有价值的托管资源(这是一个管理非托管资源本身的最终确定的黑匣子)。

于 2008-12-11T08:55:40.043 回答
-2

1) 是(通过继承)

2) 没有任何东西持有对类实例的引用(这将使它有资格进行终结)

3) 是的(为什么要实现 IDisposable,除非它要求用户显式调用 dispose?.net 中的连接类在后台使用非托管资源&如果您不调用 dispose,它将挂在它上面。由于 GC 时间未知,连接将保持打开直到那个时间)

这是我的理解。

我可能是错的。在这种情况下,专家会为我纠正问题。

于 2008-12-11T09:01:29.970 回答