C# 为什么在析构函数中调用dispose(false)?

C# 为什么在析构函数中调用dispose(false)?,c#,dispose,C#,Dispose,下面是一个典型的dispose模式示例: public bool IsDisposed { get; private set; } #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!IsDisp

下面是一个典型的dispose模式示例:

 public bool IsDisposed { get; private set; }

  #region IDisposable Members

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

  protected virtual void Dispose(bool disposing)
  {
    if (!IsDisposed)
    {
      if (disposing)
      {
        //perform cleanup here
      }

      IsDisposed = true;
    }
  }

  ~MyObject()
  {
    Dispose(false);
  }

我理解dispose的作用,但我不理解的是为什么要在析构函数中调用dispose(false)?如果你看一下定义,它将毫无用处,那么为什么会有人编写这样的代码呢?根本不从析构函数调用dispose,这有意义吗?

C#中没有析构函数。这是一个终结器,这是另一回事

区别在于您是否需要清理托管对象。您不希望尝试在终结器中清理它们,因为它们本身可能已经终结


我最近碰巧看到了《C#编程指南》的一页。这表明我在上面的回答中错了。特别是,析构函数和终结器之间有一个区别:

class Car
{
    ~Car()  // destructor
    {
        // cleanup statements...
    }
}
相当于

protected override void Finalize()
{
    try
    {
        // Cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}
“这里的想法是 Dispose(布尔)知道它是否为 被调用进行显式清理 (布尔值为真)与被 由于垃圾收集而调用 (布尔值为false)。这是 区别是有用的,因为 被显式地处理后 Dispose(Boolean)方法可以安全地 使用引用类型执行代码 引用其他对象的字段 肯定知道这些 对象尚未最终确定或删除 已处理。当布尔值为 false,Dispose(布尔)方法 不应执行引用的代码 引用类型字段,因为 对象可能已被删除 最后定稿。”

有很多(很多!)更多的信息在网上


编辑:链接。

在if(disposing)中,您应该对具有非托管资源(例如数据库连接)的托管对象调用dispose/close。当调用终结器时,这些对象不再是可访问的,因此对象本身可以被终结,您不需要对它们调用dispose。此外,终结的顺序尚未确定,因此您可能正在对已处置的对象调用dispose。

我认为造成混淆的原因是,在您的示例中,您没有释放任何非托管资源。当通过垃圾收集调用dispose时,也需要释放它们,它们将被释放到检查
disposing
之外。请参阅有关的MSDN示例。另一个将/应该在检查之外发生的是对任何基类Dispose方法的调用

从引用的文章中:

   protected override void Dispose(bool disposing) 
   {
      if (disposing) 
      {
         // Release managed resources.
      }
      // Release unmanaged resources.
      // Set large fields to null.
      // Call Dispose on your base class.
      base.Dispose(disposing);
   }

如果由于某种原因未正确处理对象,终结器将用作回退。通常会调用
Dispose()
方法,该方法删除终结器连接,并将对象转换为垃圾收集器可以轻松删除的常规托管对象

下面是来自MSDN的一个类的示例,该类具有要清理的托管和非托管资源

请注意,只有当
disposing
为true时,托管资源才会被清除,但非托管资源始终会被清除

public class MyResource: IDisposable
{
    // Pointer to an external unmanaged resource.
    private IntPtr handle;
    // Other managed resource this class uses.
    private Component component = new Component();
    // Track whether Dispose has been called.
    private bool disposed = false;

    // The class constructor.
    public MyResource(IntPtr handle)
    {
        this.handle = handle;
    }

    // Implement IDisposable.
    // Do not make this method virtual.
    // A derived class should not be able to override this method.
    public void Dispose()
    {
        Dispose(true);
        // This object will be cleaned up by the Dispose method.
        // Therefore, you should call GC.SupressFinalize to
        // take this object off the finalization queue
        // and prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    // Dispose(bool disposing) executes in two distinct scenarios.
    // If disposing equals true, the method has been called directly
    // or indirectly by a user's code. Managed and unmanaged resources
    // can be disposed.
    // If disposing equals false, the method has been called by the
    // runtime from inside the finalizer and you should not reference
    // other objects. Only unmanaged resources can be disposed.
    private void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if(!this.disposed)
        {
            // If disposing equals true, dispose all managed
            // and unmanaged resources.
            if(disposing)
            {
                // Dispose managed resources.
                component.Dispose();
            }

            // Call the appropriate methods to clean up
            // unmanaged resources here.
            // If disposing is false,
            // only the following code is executed.
            CloseHandle(handle);
            handle = IntPtr.Zero;

            // Note disposing has been done.
            disposed = true;

        }
    }

    // Use interop to call the method necessary
    // to clean up the unmanaged resource.
    [System.Runtime.InteropServices.DllImport("Kernel32")]
    private extern static Boolean CloseHandle(IntPtr handle);

    // Use C# destructor syntax for finalization code.
    // This destructor will run only if the Dispose method
    // does not get called.
    // It gives your base class the opportunity to finalize.
    // Do not provide destructors in types derived from this class.
    ~MyResource()
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }
}

以下示例演示如何创建实现IDisposable接口的资源类:


在Dispose(bool disposing)函数中:如果disposing等于true,则代码已直接或间接调用该方法。可以释放托管和非托管资源。如果disposing等于false,则该方法已由运行时从终结器内部调用,您不应引用其他对象。只有非托管资源可以被释放。

那么为什么不一起省略调用呢?但是释放是在If循环中完成的,当传递的参数为False时(从终结器中)不会执行If循环我的回答与我对另一个人的回答相同:那为什么要从终结器中调用它?@ryeguy,因为你实际上不应该自己实现终结器,除非你真的必须这样做。你的“更多信息”链接很棒!但是请注意,如果您没有非托管资源,那么
Dispose(false)
完全没有什么作用-因此您根本不需要终结器或
Dispose(bool)
。我觉得标准模式过于复杂,无法满足几乎从未出现过的用例(当它们出现时,可能是个坏主意)。这里有一个我更喜欢的:@romkyns“[IDisposable]的主要用途是释放非托管资源。”()因此,如果您没有非托管资源,那么实现IDisposable的标准方法超出了您的需要也就不足为奇了。我不确定你所说的“几乎从不发生的用例”是什么意思——混合使用托管和非托管资源并不是一个模糊的用例。也就是说,我同意如果终结器没有做任何事情(因为你没有非托管资源),那么你就不需要它。像大多数模式一样(我想,像大多数东西一样),只有在有意义的地方使用它才有意义@TimGoodman的想法是,当一个类包含多个非托管资源时,几乎不可能提供预期的保证。阅读我发布的链接,它可能会说服你;写得很好。因此,建议永远不要在类中直接使用非托管资源,而是创建一个专用包装器,其唯一目的是处置非托管资源。那么您只有两种情况:一个包装类只有一个非托管资源,或者一个托管类只有托管IDisPoables可处置。@RomanStarkov的链接已移动到派生类的
~Derived(){dispose(false);}