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?
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);
}
}
"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 more info in the “Dispose, Finalization, and Resource Management Design Guidelines”.
Edit: link.
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();
}
}
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);
}
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.
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.