当我们没有析构函数时,为什么要调用SuppressFinalize

时间:2010-04-09 06:14:12

标签: c# .net garbage-collection finalizer suppressfinalize

我的问题很少,我无法得到正确答案。

1)当我们没有析构函数时,为什么要在Dispose函数中调用SuppressFinalize。

2)Dispose和finalize用于在对象被垃圾回收之前释放资源。无论是托管资源还是非托管资源我们都需要释放它,那么为什么我们需要在dispose函数中使用一个条件,当我们从IDisposable调用这个重写函数时传递'true':从finalize调用时Dispose并传递false。

请参阅我从网上复制的以下代码。

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);
     }
   }

如果我删除了布尔保护的Dispose函数并实现如下所示。

   class Test : IDisposable
   {
     private bool isDisposed = false;

     ~Test()
     {
       Dispose();
     }


     public void Dispose()
     {
      // Code to dispose the managed resources of the class
      // Code to dispose the un-managed resources of the class
      isDisposed = true;

      // Call this since we have a destructor . what if , if we don't have one 
       GC.SuppressFinalize(this);
     }
   }       

5 个答案:

答案 0 :(得分:22)

我在这里走出困境,但......大多数人需要完整的处置模式。它可以直接访问非托管资源(通常通过IntPtr)并面向继承。大多数情况下,这些都不是必需的。

如果您只是持有对实现IDisposable的其他内容的引用,那么您几乎肯定不需要终结器 - 无论什么资源直接保存资源都负责处理。你可以做这样的事情:

public sealed class Foo : IDisposable
{
    private bool disposed;
    private FileStream stream;

    // Other code

    public void Dispose()
    {
        if (disposed)
        {
            return;
        }
        stream.Dispose();
        disposed = true;
    }
}

请注意,此不是线程安全的,但这可能不是问题。

通过不必担心子类直接保存资源的可能性,您不需要抑制终结器(因为没有终结器) - 并且您不需要提供自定义处理的子类方法无论是。没有继承,生活就会变得简单。

如果你需要允许不受控制的继承(即你不愿意打赌子类会有非常特殊的需求)那么你需要寻找完整的模式。

请注意,使用.NET 2.0中的SafeHandle,您需要自己的终结器,而不是.NET 1.1中的终结器。


要解决为什么首先出现disposing标志的问题:如果您在终结器中运行,则您引用的其他对象可能已经完成。你应该让他们自己清理,你应该只清理直接拥有的资源。

答案 1 :(得分:5)

保留第一个版本,它更安全,是配置模式的正确实现。

  1. 调用SuppressFinalize告诉GC你已经完成了所有的破坏/处理(你的类所拥有的资源)并且它不需要调用析构函数。

  2. 如果使用您的类的代码已经调用dispose,您需要进行测试,并且不应该告诉GC再次处置。

  3. 请参阅this MSDN文档(Dispose方法应调用SuppressFinalize)。

答案 2 :(得分:3)

以下是主要事实

1)Object.Finalize是你的类在具有Finalizer时重写的内容。 ~TypeName()析构函数方法只是'覆盖Finalize()'等的简写

2)如果在最终化之前处理Dispose方法中的资源(即从使用块等出来时),则调用GC.SuppressFinalize。如果您没有Finalizer,那么您不需要这样做。如果你有一个Finalizer,这可以确保该对象从Finalization队列中取出(因此我们不会处理两次,因为Finalizer通常也会调用Dispose方法)

3)您将Finalizer实现为“故障安全”机制。确保终结器运行(只要CLR没有中止),因此它们允许您确保在未调用Dispose方法的情况下清除代码(可能程序员忘记在'using'中创建实例阻止等。

4)终结器很昂贵,因为具有终结器的类型不能在Generation-0集合中进行垃圾收集(效率最高),并且在F-Reachable队列中引用它们被提升为Generation-1,因此它们代表GC根。直到GC执行Generation-1集合才能调用终结器,并释放资源 - 所以只有在非常重要时才实现终结器 - 并确保需要Finalization的对象尽可能小 - 因为所有对象都可以你的终结对象将被提升到第一代。

答案 3 :(得分:3)

<强> 1。回答第一个问题

基本上,如果您的类没有finalize方法(Destructor),则不必调用SuppressFinalize方法。我相信人们甚至在没有确定方法的情况下也会调用SupressFinalize。

<强> 2。回答第二个问题

Finalize方法的目的是释放未受管理的资源。最重要的是要理解,当对象在终结队列中时,会调用Finalize方法。垃圾收集器收集可以销毁的所有对象。垃圾收集器在销毁之前将已完成化的对象添加到终结队列。还有另一个.net后台进程为最终化队列中的对象调用finalize方法。到后台进程执行finalize方法时,该特定对象的其他托管引用可能已被销毁。因为在完成执行时没有特定的顺序。因此,Dispose Pattern希望确保finalize方法不会尝试访问托管对象。这就是为什么托管对象进入“if(disposing)”子句,这对于finalize方法是无法访问的。

答案 4 :(得分:1)

你应该总是调用SuppressFinalize(),因为你可能拥有(或将来有)实现Finalizer的派生类 - 在这种情况下你需要它。

假设您有一个没有Finalizer的基类 - 并且您决定不调用SuppressFinalize()。然后3个月后,您添加一个添加Finalizer的派生类。您可能会忘记进入基类并添加对SuppressFinalize()的调用。如果没有终结器,调用它是没有害处的。

我建议的IDisposable模式发布在这里:How to properly implement the Dispose Pattern