【问题标题】:Why call dispose(false) in the destructor?为什么在析构函数中调用 dispose(false)?
【发布时间】:2010-10-12 08:10:43
【问题描述】:

下面是一个典型的 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) ?如果您查看定义,它绝对不会做任何事情,那么为什么有人会编写这样的代码呢? not 从析构函数中调用 dispose 不是很有意义吗?

【问题讨论】:

    标签: c# dispose


    【解决方案1】:

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

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

    请注意,仅当disposing 为真时才清理托管资源,但始终清理非托管资源。

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

    【讨论】:

    • 但是请注意,如果您没有非托管资源,那么Dispose(false) 完全没有可做的事情——因此您根本不需要终结器或Dispose(bool)。我觉得标准模式过于复杂,无法满足几乎从未发生过的用例(当它们发生时,它们可能是个坏主意)。这是我更喜欢的一个:nitoprograms.blogspot.com/2009/08/…
    • @romkyns “[IDisposable] 的主要用途是释放非托管资源。” (msdn.microsoft.com/en-us/library/System.IDisposable.aspx) 因此,如果您没有非托管资源,那么实现 IDisposable 的标准方法超出您的需要也就不足为奇了。我不确定您所说的“几乎从未发生过的用例”是什么意思——混合使用托管和非托管资源并不是一个晦涩难懂的用例。
    • 也就是说,我同意如果终结器没有做任何事情(因为您没有非托管资源),那么您就不需要它。像大多数模式一样(我猜像大多数东西一样),只有在有意义的地方使用它才有意义。 ;)
    • @TimGoodman 这个想法是,当一个类包含多个非托管资源时,几乎不可能提供人们可能期望的保证。阅读我发布的链接,它可能会说服您;它写得很好。因此,建议永远不要在类中直接拥有非托管资源,而是创建一个专用包装器,其唯一目的是处置非托管资源。那么你只有两种情况:一个包含单个非托管资源的包装类,或者一个只包含托管 IDisposables 的托管类。
    • @RomanStarkov 的链接已移至blog.stephencleary.com/2009/08/…
    【解决方案2】:

    “这里的想法是 Dispose(Boolean) 知道是否是 被要求进行显式清理 (布尔值是真的)与存在 由于垃圾收集而被调用 (布尔值是假的)。这 区分是有用的,因为当 被明确处置, Dispose(Boolean) 方法可以安全地 使用引用类型执行代码 引用其他对象的字段 确定地知道这些其他 对象尚未最终确定或 处置了呢。当布尔值是 false,Dispose(Boolean) 方法 不应执行引用的代码 引用类型字段,因为那些 对象可能已经 最终确定。”

    “Dispose, Finalization, and Resource Management Design Guidelines” 中有很多(很多!)更多信息。

    编辑:链接。

    【讨论】:

    • 我的回应方式与我对其他人的反应相同:为什么还要从终结器中调用它?
    • @ryeguy 因为你实际上不应该自己实现终结器,除非你真的必须这样做。
    • 您的“更多信息”链接很棒!
    【解决方案3】:

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

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


    我最近刚好看到 C# Programming Guide 的 Destructors 页面。这表明我在上面的回答中是错误的。特别是析构函数和终结函数是有区别的:

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

    等价于

    protected override void Finalize()
    {
        try
        {
            // Cleanup statements...
        }
        finally
        {
            base.Finalize();
        }
    }
    

    【讨论】:

    • 那么为什么不一起省略调用呢?
    • 但是处理是在If循环内部完成的,当传递的参数为False时不会执行(来自终结器)
    【解决方案4】:

    我认为混淆是由于在您的示例中您没有释放任何非托管资源。这些也需要在通过垃圾收集调用 dispose 时释放,并且它们将在 disposing 检查之外释放。请参阅与 releasing unmanaged resources 相关的 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);
       }
    

    【讨论】:

    • 派生的~Derived() { Dispose(false); } 怎么样?
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      以下示例演示如何创建实现 IDisposable 接口的资源类:https://msdn.microsoft.com/en-us/library/System.IDisposable.aspx

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-04
        • 2020-08-01
        • 2019-11-09
        • 2014-03-05
        • 2018-05-24
        • 2015-08-21
        • 1970-01-01
        相关资源
        最近更新 更多