我有一个抽象基类,它实现了IDisposable以及完整的bool disposed = false
,Dispose()
和Dispose(bool)
模式,除了析构函数。基类实现了IDisposable,因为它的许多派生类需要释放非托管资源。但是,我听说带有析构函数的类很昂贵,因此如果包含了析构函数,那么使得没有非托管资源的派生类不必要地变得非常昂贵。我对此事感到困惑。我应该或不应该包括析构函数,为什么?感谢。
答案 0 :(得分:4)
如果要实现一种全新的非托管资源,则只需要包含析构函数/终结器。因此,如果您只是包装或继承现有数据库连接类型,套接字类型,gdi资源等,那么您不需要析构函数。原始类型中的析构函数将负责最终为您释放该资源。但是如果你从头开始为一个全新的数据库实现类似ADO.Net提供者对象的东西,那么你可能希望为你的连接类型实现一个析构函数,这样它就可以在最终收集时释放它的连接。 / p>
答案 1 :(得分:3)
理想情况下,Dispose模式依赖于终结器也是完整的。原因是要确保清理非托管资源。这里的技巧是在Dispose方法中你还应该进行以下调用:GC.SuppressFinalize(this),它指示垃圾收集器不以特殊方式处理实例,这将使你免于完成的开销。因此,如果用户每次都正确地处理对象(比如包装在使用块中的每个用法中),那么终结器将不会被调用,因此根本不会影响性能。
答案 2 :(得分:1)
我听说带有析构函数的类很贵
具有IDisposable
的终结器或实现的类并不比没有它们的类更昂贵。但是,实现IDisposable
的类告诉调用者他们需要在不再需要时跟踪和清理。这对调用者来说是额外的工作,但不这样做的代价是资源泄漏,至少在课程被垃圾收集之前。
简而言之,如果您的类不使用任何需要清理的资源,通常采用也实现IDisposable的字段形式,则不需要终结器。
答案 3 :(得分:1)
为清理目的而应覆盖Finalize
的唯一类是直接从Object
派生并希望清理自己的资源的类,或SafeHandle
之类的,目的是管理资源清理。否则,如果派生的非托管资源需要在Finalize
中清除但基类不需要,那么正确的方法通常是将每个单独的资源封装在自己的可终结对象中。具有终结器的对象应该避免对完成所不需要的任何对象保持强引用,因为它们持有引用的所有对象,以及任何这些对象保持引用的所有对象等都将保持活动状态。额外的垃圾收集生成。如果一个拥有许多其他对象链接的对象George
持有对一个不具有强反向链接的可终结对象的引用,并且George
被放弃,则需要保留最终对象对于额外的GC生成,George
以及它所持有的其他对象不会直接和间接引用。相比之下,如果George
本身实现了Finalize
,那么它和它拥有直接或间接引用的每个对象都必须保持不变。
此外,如果最终使用大型可终结对象实际上是使用其中一个资源,那么最终确定有时会导致一些罕见但难以追踪的Hindenbug。使用可以通过Finalize
清理的资源的代码必须确保包含这些资源的对象在这些资源仍在使用时不符合最终确定条件。这通常使用GC.KeepAlive()
来完成。如果派生类添加了Finalize
方法,该方法清除父类中存在的任何资源,但父类不希望清理终结器,则可能会发生错误。将资源封装在它们自己的类中可以避免这个问题(在资源使用时可能会对父对象进行垃圾收集,但是如果封装对象设计得当,则无关紧要 - 封装对象的{{ 1}}方法在其方法使用完资源后才会运行。