我知道在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的回复。
感谢。
答案 0 :(得分:16)
问题1和2 :CLR基本上检查终结器是否被覆盖。如果不是,则将其视为没有终结器。
在System.Object中使用终结器的好处是编译器知道他们可以总是调用base.Finalize()
。这可以避免版本控制问题。考虑一个没有System.Object.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)
Finalize()
才会被CLR计算。更新:每个班级都负责管理自己的资源。由于面向对象范例的抽象和封装特性,类的消费者不应该间接关注它拥有的资源。因此,您应该手动释放您拥有的资源(您拥有的资源就是直接拥有,因为您将其他内容视为黑匣子)或将其留给GC发布它们。对于非托管资源,您无法将其保留到GC,因此必须手动释放它。从这个意义上讲,Jon提到的SafeHandle是非托管资源的托管抽象,因此它应被视为有价值的托管资源(这是一个黑盒子,用于管理非托管资源本身的最终确定) )。
答案 3 :(得分:-2)
1)是(凭借继承)
2)没有任何东西持有对类实例的引用(这将使其有资格完成)
3)是(为什么一个人应该实现IDisposable,除非它要求用户明确地调用dispose?.net中的连接类使用一个非管理资源&如果你不调用dispose,它会挂起由于GC时间未知,连接将保持打开状态直到那时)
这是我的理解。
我可能是错的。 在这种情况下,专家会为我纠正错误。