Running with Code Like with scissors, only more dangerous

3Feb/080

Looking at .NET: The Disposable Pattern

One of the more obscure things about the .NET Framework is the Disposable pattern used throughout the framework, supported via the IDisposable interface.  This pattern is so pervasive throughout .NET, that C# intrinsically supports it via the using keyword.  There is also a standard pattern for implementing the interface that the interface just can’t express (perhaps because interfaces can’t specify protected methods; maybe that’s a C# 4.0 Wishlist part 6 item?).

We can use an IDisposable object with the using keyword like so:

   1:  using (SqlConnection con = new SqlConnection(WebConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString))
   2:  using (SqlCommand cmd = new SqlCommand("[dbo].[GetAllItems]", con))
   3:  {
   4:      cmd.CommandType = CommandType.StoredProcedure;
   5:   
   6:      con.Open();
   7:      using (SqlDataReader reader = cmd.ExecuteReader())
   8:      {
   9:          return GetAllItemsFromReader(reader);
  10:      }
  11:  }

In this example, SqlConnection, SqlCommand, and SqlDataReader all implement IDisposable, because they interoperate with unmanaged code.  By using the using blocks, the C# compiler actually transforms these into try/catch blocks:

   1:  List<Item> _compilerGeneratedResult;
   2:  SqlConnection con = null;
   3:  SqlCommand cmd = null;
   4:  try 
   5:  {
   6:      con = new SqlConnection(WebConfigurationManager.ConnectionStrings["DbConnection"].ConnectionString;
   7:      cmd = new SqlCommand("[dbo].[GetAllItems]", con);
   8:   
   9:      cmd.CommandType = CommandType.StoredProcedure;
  10:   
  11:      con.Open();
  12:   
  13:      SqlDataReader reader = null;
  14:      try
  15:      {
  16:          reader = cmd.ExecuteReader();
  17:          _compilerGeneratedResult = GetAllItemsFromReader(reader);
  18:      }
  19:      finally
  20:      {
  21:          reader.Dispose();
  22:      }
  23:  }
  24:  finally
  25:  {
  26:      if (cmd != null)
  27:          cmd.Dispose();
  28:      if (con != null)
  29:          con.Dispose();
  30:  }
  31:   
  32:  return _compilerGeneratedResult;

Yeah, if it was left up to the C# users to use this pattern correctly, we’d never do it (not without the using keyword, anyway).  But the question is, how do we implement it?

Traditionally, we implement it by creating a protected, virtual method (or private method if the class is sealed), that accepts a boolean value that indicates whether we’re calling this method via Dispose() or via the destructor.  The Dispose() method calls this with true, and we also implement a destructor that calls it with false.  The Dispose(bool) method then implements the cleanup logic, and if the parameter is true, also tells the garbage collector to not perform the invoke the finalizer (the destructor) on this object.  Here’s a sample:

   1:  using System;
   2:  using System.Runtime.InteropServices;
   3:   
   4:  namespace DisposableSample
   5:  {
   6:      public class HGlobalPtr : IDisposable
   7:      {
   8:          #region IDisposable Members
   9:   
  10:          ~HGlobalPtr()
  11:          {
  12:              Dispose(false);
  13:          }
  14:   
  15:          public void Dispose()
  16:          {
  17:              Dispose(true);
  18:          }
  19:   
  20:          protected virtual void Dispose(bool disposing)
  21:          {
  22:              if (disposing)
  23:                  GC.SuppressFinalize(this);
  24:          }
  25:   
  26:          #endregion
  27:      }
  28:  }

This is the most basic implementation of the IDisposable pattern.  We’re going to evolve it a bit, actually add use to it, and also look at Static Code Analysis (SCA) output from this.  First, to the SCA (using FxCop).

This basic example generates two warnings, incidentally, from the same rule.  Here they are:

warning : CA1816 : Microsoft.Usage : Change ‘HGlobalPtr.Dispose()’ to call ‘GC.SuppressFinalize(object)’. This will prevent unnecessary finalization of the object once it has been disposed and it has fallen out of scope.

warning : CA1816 : Microsoft.Usage : ‘HGlobalPtr.Dispose(bool)’ calls ‘GC.SuppressFinalize(object)’, a method that is typically only called within an implementation of ‘IDisposable.Dispose’. Refer to the IDisposable pattern for more information.

If you look at help for this rule, you’ll see that to properly implement this change, you actually call GC.SuppressFinalize(this) within the Dispose() method (as opposed to Dispose(bool) method).  Doing so ensures that GC.SuppressFinalize is called via IDisposable.Dispose even if derived classes override the virtual Dispose(bool) method.

Here’s a more complete implementation of HGlobalPtr, with IDisposable fully implemented:

   1:      public class HGlobalPtr : IDisposable
   2:      {
   3:          private IntPtr m_ptr;
   4:   
   5:          public HGlobalPtr(int size)
   6:          {
   7:              m_ptr = Marshal.AllocHGlobal(size);
   8:          }
   9:   
  10:          #region IDisposable Members
  11:   
  12:          ~HGlobalPtr()
  13:          {
  14:              Dispose(false);
  15:          }
  16:   
  17:          public void Dispose()
  18:          {
  19:              Dispose(true);
  20:              GC.SuppressFinalize(this);
  21:          }
  22:   
  23:          protected virtual void Dispose(bool disposing)
  24:          {
  25:              if (disposing)
  26:              {
  27:                  // free the state of any contained objects
  28:                  // we don't contain any other objects!
  29:              }
  30:              // free my own state
  31:              if (m_ptr != IntPtr.Zero)
  32:              {
  33:                  Marshal.FreeHGlobal(m_ptr);
  34:                  m_ptr = IntPtr.Zero;
  35:              }
  36:          }
  37:   
  38:          #endregion
  39:      }

Now, there are other code analysis problems; I need to handle security warnings for calls to Marshal.AllocHGlobal and Marshal.FreeHGlobal, which have a LinkDemand permission set on them.  I should also consider replacing m_ptr with a SafeHandle.  But aside from these, IDisposable is correctly implemented.

Just remember – if your object is IDisposable – please, use the using () statement!

For more information on implementing IDisposable, refer to the Microsoft documentation article on Technet.

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

ERROR: si-captcha.php plugin says GD image support not detected in PHP!

Contact your web host and ask them why GD image support is not enabled for PHP.

ERROR: si-captcha.php plugin says imagepng function not detected in PHP!

Contact your web host and ask them why imagepng function is not enabled for PHP.

No trackbacks yet.