C# 抽象基类中的IDisposable和析构函数

C# 抽象基类中的IDisposable和析构函数,c#,destructor,idisposable,unmanagedresources,C#,Destructor,Idisposable,Unmanagedresources,我有一个抽象基类,它实现IDisposable和完整的bool disposed=false,Dispose(),以及Dispose(bool)模式(析构函数除外)。基类实现IDisposable,因为它的许多派生类需要释放非托管资源。但是,我听说带有析构函数的类很昂贵,因此如果我包含析构函数,那么没有非托管资源的派生类将变得不必要的昂贵。我对这件事感到困惑。我应该还是不应该包括析构函数,为什么?谢谢。如果要实现一种全新的非托管资源,只需包含析构函数/终结器。因此,如果您只是包装或继承现有的数据

我有一个抽象基类,它实现IDisposable和完整的
bool disposed=false
Dispose()
,以及
Dispose(bool)
模式(析构函数除外)。基类实现IDisposable,因为它的许多派生类需要释放非托管资源。但是,我听说带有析构函数的类很昂贵,因此如果我包含析构函数,那么没有非托管资源的派生类将变得不必要的昂贵。我对这件事感到困惑。我应该还是不应该包括析构函数,为什么?谢谢。

如果要实现一种全新的非托管资源,只需包含析构函数/终结器。因此,如果您只是包装或继承现有的数据库连接类型、套接字类型、gdi资源等,那么您确实不需要析构函数。原始类型中的析构函数将负责最终为您释放该资源。但是,如果您从头开始为一种全新的数据库实现类似ADO.Net provider对象的东西,那么您可能希望为您的连接类型实现析构函数,以便在最终收集连接时释放它的连接

我听说带析构函数的类很昂贵

带有终结器或IDisposable实现的类并不比没有终结器或实现的类更昂贵。然而,一个实现了
IDisposable
的类告诉调用者,当不再需要时,需要跟踪并清理它们。这对调用方来说是额外的工作,但不这样做的代价是资源泄漏,至少在类被垃圾收集之前是如此


简言之,如果您的类不使用任何需要清理的资源(通常以也实现IDisposable的字段的形式),则不需要终结器

理想情况下,Dispose模式也依赖于终结器来完成。原因是要确保非托管资源将被清理。这里的诀窍是,在Dispose方法中,您还应该有以下调用:GC.SuppressFinalize(this),它指示垃圾收集器不要以特殊方式处理实例,这将使您免受终结的开销。因此,如果用户每次都正确地处理对象(就像在using块中的每次使用中包装)那么就不会调用终结器,因此根本不会影响性能。

为了清理的目的,应该覆盖
Finalize
的类只有那些直接从
对象派生并希望清理自己资源的类,或者类似于
安全句柄的类,其目的是管理资源清理。否则,如果派生类将具有需要在
Finalize
中清理的非托管资源,但基类不会,则正确的方法通常是将每个单独的资源封装在其自己的finalizable对象中。具有终结器的对象应避免持有对终结不需要的任何对象的强引用,因为它们持有引用的所有对象以及这些对象持有引用的所有对象等将保持活动状态,以便生成额外的垃圾收集。如果一个对象
George
持有指向许多其他对象的链接,而该对象持有指向一个可终结对象的引用,而该对象不具有强反向链接,并且
George
被放弃,可终结的对象将需要保留一个额外的GC生成,但它持有直接和间接引用的其他对象将不会保留。相比之下,如果乔治
自己实现了
定版,那么它和它直接或间接引用的每个对象都必须保留在周围


此外,如果最后一次使用大型可终结对象实际上是使用了它的一种资源,那么终结有时会导致一些罕见但难以追踪的印地语。使用可通过Finalize清理的资源的代码必须确保包含这些资源的对象在这些资源仍在使用时没有资格进行终结。这通常通过使用
GC.KeepAlive()
完成。如果派生类添加一个
Finalize
方法来清理父类中存在但父类不希望终结器清理的任何资源,则可能会发生错误。将资源封装在自己的类中可以避免这个问题(在使用资源时,父对象可能会被垃圾收集,但如果封装对象设计正确,这无关紧要——封装对象的
Finalize
方法在其方法使用完资源后才会运行).

+1.有用。对于未来的答案,请避免添加签名,因为签名已经存在,并且很少在答案中提供其他信息。