Why call dispose(false) in the destructor?
Asked Answered
B

6

69

What follows is a typical dispose pattern example:

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

I understand what dispose does, but what I don't understand is why you would want to call dispose(false) in the destructor? If you look at the definition it would do absolutely nothing, so why would anyone write code like this? Wouldn't it make sense to just not call dispose from the destructor at all?

Bergen answered 10/3, 2009 at 2:49 Comment(0)
S
52

The finalizer is used as a fall-back if the object is not disposed properly for some reason. Normally the Dispose() method would be called which removes the finalizer hookup and turns the object into a regular managed object that the garbage collector easily can remove.

Here is an example from MSDN of a class that has managed and unmanaged resources to clean up.

Notice that the managed resources are only cleaned up if disposing is true, but unmanaged resources is always cleaned up.

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);
    }
}
Sebiferous answered 10/3, 2009 at 3:11 Comment(11)
But observe that if you don't have unmanaged resources then Dispose(false) has exactly nothing to do - so you don't need a finalizer nor a Dispose(bool) at all. I feel the standard pattern is overly complicated to cater for use cases that almost never occur (and when they do they are probably a bad idea). Here's one I much prefer: nitoprograms.blogspot.com/2009/08/…Chaisson
@romkyns "The primary use of [IDisposable] is to release unmanaged resources." (msdn.microsoft.com/en-us/library/System.IDisposable.aspx) So it's no surprise that the standard way of implementing IDisposable is more than you need if you don't have unmanaged resources. I'm not sure what you mean by "use cases that almost never occur" -- having a mix of managed and unmanaged resources is not an obscure use case.Unrealizable
That said, I agree that if the finalizer isn't doing anything (because you have no unmanaged resources) then you don't need it. Like most patterns (like most anything, I guess), it only makes sense to use it where it makes sense. ;)Unrealizable
@TimGoodman The idea is that it is nearly impossible to provide the guarantees one might expect when a class contains multiple unmanaged resources. Read the link I posted, it might convince you; it's very well written. The suggestion is to therefore never ever have unmanaged resources directly in a class, and instead create a special-purpose wrapper whose only purpose is to dispose of the unmanaged resource. Then you have only two cases: a wrapper class with a single unmanaged resource, or a managed class with only managed IDisposables to dispose of.Chaisson
private void Dispose(bool disposing) should be virtualAlongside
@RoyiNamir: If the method is private it can't be virtual. In later versions of the documentation it uses a protected virtual method.Sebiferous
@Sebiferous Of course it can't. I was just referencing that method which according to msdn should be virtual (protected). I was just copying and pasting so you would know on which method I'm talking about.Alongside
@RoyiNamir: Have you found any documentatation that says that the method should be virtual? Microsoft hasn't changed the documentation for version 1.1, 2.0, 3.0 and 3.5 where it uses a private method: msdn.microsoft.com/en-us/library/…Sebiferous
@Sebiferous Richter also provides the protected virtual in his book but he says it is strongly discouraged to override this method.Alongside
See msdn.microsoft.com/en-us/library/fs2xkftw%28v=vs.110%29.aspx and msdn.microsoft.com/en-us/library/b1yfkh5e%28v=vs.110%29.aspxUnpaidfor
@RomanStarkov 's link has moved to blog.stephencleary.com/2009/08/…Thermoelectric
N
21

"The idea here is that Dispose(Boolean) knows whether it is being called to do explicit cleanup (the Boolean is true) versus being called due to a garbage collection (the Boolean is false). This distinction is useful because, when being disposed explicitly, the Dispose(Boolean) method can safely execute code using reference type fields that refer to other objects knowing for sure that these other objects have not been finalized or disposed of yet. When the Boolean is false, the Dispose(Boolean) method should not execute code that refer to reference type fields because those objects may have already been finalized."

There's much (much!) more info in the “Dispose, Finalization, and Resource Management Design Guidelines”.

Edit: link.

Negate answered 10/3, 2009 at 2:54 Comment(3)
I respond the same way I did to the other guy: why call it at all then from the finalizer?Bergen
@Bergen because you should actually not be implementing the finalizeer yourself unless you really really have to.Sip
Your "much more info" link is great!Requiem
J
9

There are no destructors in C#. That's a Finalizer, which is a different thing.

The distinction is whether you need to clean up managed objects or not. You don't want to try to clean them up in the finalizer, as they may themselves have been finalized.


I just recently happened to look at the Destructors page of the C# Programming Guide. It shows that I was mistaken in my answer, above. In particular, there is a difference between destructor and finalizer:

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

is equivalent to

protected override void Finalize()
{
    try
    {
        // Cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}
Jewbaiting answered 10/3, 2009 at 2:53 Comment(2)
so why not just omit the call all together?Bergen
But the disposing is done inside the If loop, which will not execute when the parameter passed is False (as from the finalizer)Bergen
J
3

I think the confusion is due to the fact that in your example you aren't releasing any unmanaged resources. These also need to be released when dispose is called via garbage collection and they would be released outside the check for disposing. See the MSDN example relating to releasing unmanaged resources. The other that that would/should happen outside the check is a call to any base class Dispose method.

From the quoted article:

   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);
   }
Jutland answered 10/3, 2009 at 3:10 Comment(1)
What about ~Derived() { Dispose(false); } for derived ?Alongside
Z
2

Inside the if(disposing) you are supposed to call dispose/close on managed objects that have unmanaged resources (e.g. database connections).When the finalizer is called these objects are not longer reachable so the objects themselves can be finalized and you don't need to call dispose on them. Also the order of finalization is undeterminated so you may be calling dispose on already disposed objects.

Zama answered 10/3, 2009 at 2:59 Comment(0)
M
1

The following example demonstrates how to create a resource class that implements the IDisposable interface: https://msdn.microsoft.com/en-us/library/System.IDisposable.aspx

In Dispose(bool disposing) function: If disposing equals true, the method has been called directly or indirectly by your 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.

Myocarditis answered 21/5, 2018 at 10:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.