为什么传统的Dispose模式抑制最终化?

问题描述 投票:9回答:6

假设这是传统的Dispose模式(取自devx,但在许多网站上看到)

class Test : IDisposable
{
  private bool isDisposed = false;

  ~Test()
  {
    Dispose(false);
  }

  protected void Dispose(bool disposing)
  {
    if (disposing)
    {
      // Code to dispose the managed resources of the class
    }

    // Code to dispose the un-managed resources of the class

    isDisposed = true;
  }

  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }
}

我不明白为什么我们打电话给GC.SupressFinalize(this)。这需要我编写自己的托管资源处理,包括清空我的引用?我必须承认,我有点迷茫。有人会对这种模式有所了解吗?

理想情况下,我只想处理我的非托管资源,让GC自己进行托管收集。

实际上,我甚至不知道为什么我们指定一个终结器。在任何情况下,编码人员都应该自己处理,现在不应该他?如果这只是一个后备机制,我会删除它。

c# dispose finalize
6个回答
14
投票

使用IDisposable模式,以便在客户端代码调用Dispose方法时,对象可以确定性地清理其资源。

如果客户端代码由于某种原因无法调用Dispose,则终结器仅作为后备。

如果客户端代码调用Dispose,则会在那时执行资源的清理,并且在完成期间不需要再次执行。在这种情况下调用SuppressFinalize意味着该对象不再需要额外的GC终结成本。

而且,如果您自己的班级仅使用托管资源,则完全没必要使用终结者:GC将负责任何托管资源,让这些资源自己担心是否需要后备终结器。如果它直接处理非托管资源,您应该只考虑自己类中的终结器。


3
投票

SuppressFinalize仅抑制任何自定义终结器。

它不会改变任何其他GC行为。 您永远不需要显式地清空引用。 (除非你希望他们尽早收集)

没有任何终结器的类和你调用SuppressFinalize的实例之间没有区别。

调用SuppressFinalize可以防止额外调用Dispose(false),并使GC更快一些。 (终结者很贵)

请注意,没有非托管资源的类不应该有终结器。 (他们仍然应该调用SuppressFinalize,除非它们被密封;这允许继承的类添加非托管资源)


2
投票

如果某些派生类决定添加终结器,则存在SuppressFinalize调用。如果正常处置成功完成,则无需完成;即使派生类决定添加一个,SuppressFinalize调用也会阻止它执行并干扰垃圾收集。

要理解为什么这很重要,你应该认为finalization不是垃圾收集的一部分,而是在它之前发生的事情。当一个类注册完成时(创建时自动,如果它覆盖Finalize),它将被放入一个名为Finalization Queue的特殊列表中。 Finalization Queue中没有对象,也没有队列中对象直接或间接引用的任何对象可以被垃圾收集,但是如果发现终结队列中的任何对象没有除队列之外的有根引用,则该对象将从队列中拉出,终结器将运行。在调度终结器时,该对象将不可收集(因为在调度期间将存在引用);一旦终结器完成,通常就不再有对该对象的引用,因此它(以及由此引用的对象)通常是可收集的。

就个人而言,我认为SuppressFinalize是愚蠢的,因为我可以想到为什么派生类应该有一个终结器。如果派生类要添加父类不知道的souse非托管资源(*),则应创建另一个类以保存这些资源;父类应该持有对它的引用。这样,父类本身将不需要最终化,并且父类引用的对象不会被不必要地阻止垃圾收集。


2
投票

来自Msdn:“此方法在对象头中设置一个位,系统在调用终结器时进行检查.obj参数必须是此方法的调用者。实现IDisposable接口的对象可以从IDisposable.Dispose调用此方法防止垃圾收集器在不需要它的对象上调用Object.Finalize的方法。“

因此它阻止了来自GC的额外调用。如果从终结器方法中调用它,那么当对象被最终确定时,它就不会做任何事情,因为它已经完成了。否则,允许GC回收内存,而不会终止对象,从而使事情变得更快。


1
投票

MSDN所述,执行Finalize方法成本很高。通过调用dispose,你已经自己完成了你的课程,因此不需要调用终结器。如果您的代码永远不会直接调用Dispose(或者拥有'实例的任何人),则实现终结器。


0
投票
// If the monitor.Dispose method is not called, the example displays the following output:
//       ConsoleMonitor instance....
//       The ConsoleMonitor class constructor.
//       The Write method.
//       The ConsoleMonitor finalizer.
//       The Dispose(False) method.
//       Disposing of unmanaged resources.
//       
// If the monitor.Dispose method is called, the example displays the following output:
//       ConsoleMonitor instance....
//       The ConsoleMonitor class constructor.
//       The Write method.
//       The Dispose method.
//       The Dispose(True) method.
//       Disposing of managed resources.
//       Disposing of unmanaged resources.

来自https://msdn.microsoft.com/en-us/library/system.gc.suppressfinalize(v=vs.110).aspx

© www.soinside.com 2019 - 2024. All rights reserved.