CLR什么时候说对象有终结器?

时间:2008-12-11 08:51:39

标签: c# .net memory-management garbage-collection

我知道在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论坛中询问时,我得到了here的回复。

感谢。

4 个答案:

答案 0 :(得分:16)

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

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

  • System.Object(no Finalize)
  • Acme.BaseClass(no Finalize)
  • MyCompany.DerivedClass(Finalize)

在对象中没有Finalize方法,MyCompany.DerivedClass中的终结器无法调用任何内容。当Acme.BaseClass的第2版出现 with 终结器时会导致问题。除非你重新编译MyCompany.DerivedClass,否则DerivedClass的实例将在不调用BaseClass.Finalize的情况下完成,这显然是一件坏事。

现在考虑相同的情况 with System.Object.Finalize - 编译器在DerivedClass.Finalize中自动插入对base.Finalize的调用,在版本1中只调用System中的no-op实现。宾语。当Acme.BaseClass的第2版出来时,对base.Finalize的调用将(不重新编译DerivedClass)调用BaseClass.Finalize。

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

从.NET 2.0开始,对于SafeHandle类,您需要自己的终结器才能难以置信

答案 1 :(得分:3)

1:它只是被重写(在有用的意义上)

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

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

答案 2 :(得分:0)

  1. 不,这并不意味着。只有被覆盖的Finalize()才会被CLR计算。
  2. 通过如上定义的终结器。
  3. 不,总是是必要的。这只是一个很好的模式。我的意思是,没有人强迫你这样做。但是,如果你有非托管资源,这是一件好事,因为如果有人忘记处理它,非托管资源将在某个时候被释放。 FxCop不强制执行严格的规则。如果你不照顾,它会强制执行可能导致将来失败的良好模式。
  4. 更新:每个班级都负责管理自己的资源。由于面向对象范例的抽象和封装特性,类的消费者不应该间接关注它拥有的资源。因此,您应该手动释放您拥有的资源(您拥有的资源就是直接拥有,因为您将其他内容视为黑匣子)或将其留给GC发布它们。对于非托管资源,您无法将其保留到GC,因此必须手动释放它。从这个意义上讲,Jon提到的SafeHandle是非托管资源的托管抽象,因此它应被视为有价值的托管资源(这是一个黑盒子,用于管理非托管资源本身的最终确定) )。

答案 3 :(得分:-2)

1)是(凭借继承)

2)没有任何东西持有对类实例的引用(这将使其有资格完成)

3)是(为什么一个人应该实现IDisposable,除非它要求用户明确地调用dispose?.net中的连接类使用一个非管理资源&如果你不调用dispose,它会挂起由于GC时间未知,连接将保持打开状态直到那时)

这是我的理解。

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