IDisposable, Finalizers, Destructors,
and the Garbage Man

by Peter A. Bromberg, Ph.D.

Peter Bromberg
"It is a violation of network regulations to initiate or propagate chain letters via e-mail. Anyone violating network regulations could be denied further access to the network. Please pass this important information to all your friends." -- anonymous
When do I need to implement a Finalizer or IDisposable?
Recently another developer was experimenting with structs we are using to serialize a class for eventual mapping into an XmlDocument to transmit to a vendor. I noticed he had derived from IDisposable but had not implemented any Dispose pattern for the struct. I queried him on it, "What is the rationale for implementing IDisposable on a struct (a value type)?" (I just love to challenge other developers, even when I haven't a clue what the hell I'm talking about).

Fact of the matter is, this is one of the most esoteric areas of working with the .NET Framework, and if you search around, you will undoubtedly find a lot of people parroting something they have seen elsewhere, often without a full understanding of what they have written, which of course leads you on a wild goose chase to nowhere pretty fast.

The Nitty-Gritty: Finalizers only need to be implemented when you hold onto resources that need cleaning up.

Destructors and the Finalize Method

The IDisposable interface, Destructors, and the C# Finalize method are all intertwined. Therefore, a short discussion is in order:

A destructor is a method that is called when an object gets destroyed. In C++, destructors are used to free memory and perform other housekeeping tasks. In the .NET Framework, we have the Garbage Collector that does most of this kind of work automatically. As a general rule, unless there is a compelling case for using one, you should forget about defining a destructor and leave the cleanup stuff to the runtime.

In my neighborhood, the only thing I need to remember is that the Garbage Man comes on Thursday mornings and I'd better get my crap to the street before he arrives, or I am gonna be toast. Otherwise, I really don't have to worry about anything. .NET works pretty much the same way, with the primary rule of thumb being "if it ain't broke, then don't fix it".

In C#, the Finalize method performs the operations that a standard C++ destructor would do. When you define a finalizer in C#, you don't name it Finalize -- you use the C++ destructor syntax of placing a tilde ( ~ ) symbol before the name of the class. When the C# is compiled into IL, this is translated by the compiler into a method named Finalize, which automatically calls the base class destructor once any cleanup code is executed.

The most important point to remember if you supply a destructor is that you cannot predict when it will be called - it will only be executed when the garbage collector runs and removes it from memory. It is for this reason that if you have any expensive unmanaged resources to release when your class is destroyed, it is preferable to dispose of them in a Close() or Dispose() method that can be called explicitly by the user of the class. In actual fact, we can never guarantee that a destructor will ever be called since we can tell the garbage collector NOT to call an object's destructor with the SuppressFinalize method of the System.GC class.



Disposing managed and unmanaged resources

As most developers are aware, there are two types of resources that can be part of a C# type; those defined in the .NET Framework (managed) and unmanaged, external resources that a type may want to access. The runtime can dispose of unmanaged resources when it calls the destructor associated with our type, but we can also implement the IDisposable interface to allow users of our class to explicitly request that resources be released, instead of waiting around for our friend the Garbage Man (who usually only arrives early when you've forgotten to put out the garbage).

Example:
FileStream holds onto a native file handle and implements a finalizer to release the handle when the FileStream is garbage collected. Finalizers place a lot of pressure on the garbage collector and should be used only when absolutely needed.

The IDisposable interface tells the world that your class holds onto resources that need to be disposed and provides users a way to release them. Any class that implements a finalizer should also implement IDisposable. But, not all classes that implement IDisposable need to or even should implement a finalizer. Look at the various classes that actually have a Dispose method to get more insight (Ex. SqlConnection).

Let's say a managed class has a FileStream as a private member. FileStream holds onto unmanaged resources and implements both IDisposable and a finalizer. When no more references to the instance exist, the FileStream will be unreachable and will be available for finalization. There's no reason for the class having FileStream as a member to be in the queue of objects that have registered for finalization since the instance of FileStream will be there. But the class should provide a way for a user to immediately release all resources it holds onto, either directly or indirectly, and so the class should implement IDisposable. Your implementation of Dispose will simply call the FileStream's Dispose method. Be careful not to dispose of shared resources (resources used by other instances, for example).

If you do need to implement a finalizer in your class, your Dispose method should use the GC.SuppressFinalize method to ensure that finalization of your instance is suppressed. This will remove the instance from the set of objects that require finalization, reducing the pressure on the garbage collector during a collection. A common pattern implemented throughout the Microsoft® .NET Framework is to add to a class a Dispose method that takes a Boolean as a parameter. This Boolean indicates whether the class is being disposed because the IDisposable.Dispose method is called or because the finalizer is run (both the finalizer and IDisposable.Dispose delegate to this method). If it's being disposed deterministically, GC.SuppressFinalize is invoked. If it's being called from a finalizer, avoid using managed members of your class that implement finalizers as they may have already been finalized.

Here follows a complete example of the IDisposable pattern:

// Design pattern for the base class.
// By implementing IDisposable, you are announcing that instances
// of this type allocate scarce resources.
public class BaseResource: IDisposable
{
   // Pointer to an external unmanaged resource.
   private IntPtr handle;
   // Other managed resource this class uses.
   private Component Components;
   // Track whether Dispose has been called.
   private bool disposed = false;

   // Constructor for the BaseResource object.
   public BaseResource()
   {
      // Insert appropriate constructor code here.
   }

   // Implement IDisposable.
   // Do not make this method virtual.
   // A derived class should not be able to override this method.
   public void Dispose()
   {
      Dispose(true);
      // Take yourself off the Finalization queue 
      // to 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.
   protected virtual 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.
            Components.Dispose();
         }
         // Release unmanaged resources. If disposing is false, 
         // only the following code is executed.
         CloseHandle(handle);
         handle = IntPtr.Zero;
         // Note that this is not thread safe.
         // Another thread could start disposing the object
         // after the managed resources are disposed,
         // but before the disposed flag is set to true.
         // If thread safety is necessary, it must be
         // implemented by the client.

      }
      disposed = true;         
   }

   // 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.
   ~BaseResource()      
   {
      // Do not re-create Dispose clean-up code here.
      // Calling Dispose(false) is optimal in terms of
      // readability and maintainability.
      Dispose(false);
   }

   // Allow your Dispose method to be called multiple times,
   // but throw an exception if the object has been disposed.
   // Whenever you do something with this class, 
   // check to see if it has been disposed.
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}

// Design pattern for a derived class.
// Note that this derived class inherently implements the 
// IDisposable interface because it is implemented in the base class.
public class MyResourceWrapper: BaseResource
{
   // A managed resource that you add in this derived class.
   private ManagedResource addedManaged;
   // A native unmanaged resource that you add in this derived class.
   private NativeResource addedNative;
   private bool disposed = false;

  // Constructor for this object.
   public MyResourceWrapper()
   {
      // Insert appropriate constructor code here.
   }

   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {
               // Release the managed resources you added in
               // this derived class here.
               addedManaged.Dispose();         
            }
            // Release the native unmanaged resources you added
            // in this derived class here.
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            // Call Dispose on your base class.
            base.Dispose(disposing);
         }
      }
   }
}

// This derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits 
// them from the base class.

One final thought is that when the using statement in C# is applied to an object, the resources associated with the object are disposed of as soon as the block of code following the closing brace of the using statement is executed. It is really just compiler shorthand for a try / finally with a call to:

if (object !=null) ((IDisposable)object).Dispose();

N.B. Jon Wojtowicz made a very prescient comment below, which piqued my curiosity and prompted me to add this paragraph after firing up Reflector for a minute. Developers should be aware that the behavior of Close and Dispose methods on certain types can be very different. A prime example is the SqlConnection class. In .NET 1.1, when you call the Close method on your Connection object, you get a lot of very detailed cleanup code under the hood, and the finally block looks like this:

finally
{
if (this._sdc == null)
{
if (this != this._internalConnection.ConnectionWeakRef.Target)
{
throw SQL.ConnectionPoolingError();
}
SqlConnectionPoolManager.ReturnPooledConnection(this._internalConnection);
}
this._sdc = null;
this._internalConnection = null;
this._fIsClosing = false;
}

Note the line in blue - your connection is being explicitly returned to the connection pool. Now let's see what happens if you call Dispose instead:

protected override void Dispose(bool disposing)
{
if (disposing)
{
switch (this._objectState)
{
case ConnectionState.Open:
{
this.Close();
break;
}
}
this._constr = null;
}
base.Dispose(disposing);
}

Note above that "this._constr" is the connection string private field, and it is being set to null. Right before that is a switch statement that calls the Close method, but only if the Connection is open. Then the connection string is set to null. Before this was "fixed" in 1.1, it actually removed the connection string regardless, in effect forever preventing the connection from being returned to the pool, which depends on the connection string for everything it does! A word to the wise: At least on first blush, it looks a lot safer to call the Close method on your SqlConnections, and forget about either a first or subsequent call to Dispose. That is, if you expect Connection Pooling to work, which in the vast majority of cases, is the behavior you want.

So the short answer for the question posed at the beginning is: you will be hard - pressed to find a good reason to implement IDisposable on a struct. IDisposable is not some magical chicanery behind the scenes. It's simply an Interface that allows you to provide a standardized way for callers to tell your class they want it to go away and clean up it's room before it leaves. If it doesn't have a lot of junk in it's room to clean up, you can just close the door instead and everything will be fine. Rest assured, the room and everything in it will disappear in the next mudslide!

 


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.

Article Discussion: