'Using' the IDisposable Interface

The IDisposable interface can be used for far more than just clearing up resources. When coupled with the 'using' keyword, it enables code to be called on exiting a code block.

In C++, destructors could be used to ensure that a post condition was met when a code block exited. For instance, often we wish to set a variable at the start of a code block and then reset it when exiting. For example:

class CSetBoolean
{
public:
   CSetBoolean(BOOL &rfBoolean, BOOL fValue)
      : m_rfBoolean(rfBoolean), m_fValue(rfBoolean)
   {
      m_rfBoolean = fValue;
   }

   virtual ~CSetBoolean()
   {
      m_rfBoolean = m_fValue;
   }

private:
   BOOL &m_rfBoolean;
   BOOL m_fValue;
};

As an example of use, consider the following:

class CExample
{
public:
   CExample()
   {
      m_fValue = FALSE;
   }

   BOOL Function()
   {
      CSetBoolean setBool(m_fValue, TRUE);

      if (!Function1())
      {
         return FALSE;
      }

      if (!Function2())
      {
         return FALSE;
      }

      if (!Function3())
      {
         return FALSE;
      }

      return TRUE;
   }

private:
   BOOL m_fValue;

   BOOL Function1();
   BOOL Function2();
   BOOL Function3();
}

The boolean is set using the CSetBoolean class, which in turn returns the original value on destruction. In this way, it doesn't matter where Function() exits—the boolean value is always returned to its original state. This facilitates robust code because we know that the post condition for Function() returning m_fValue back to its original state is always met.

The C# Way

So, as the above design method leads to more robust code, can we do a similar thing in C#?

Initially, you might think not. All objects in .NET are created and destroyed using the garbage collector. The time at which garbage collection takes place is arbitrary (unless you explicitly force it), so we can't use the destructor (or finalizer) of a C# class to perform the above operation.

For more information about garbage collection see here and here.

However, this is where the IDisposable interface, coupled with the 'using' keyword, comes to the fore.

IDisposable

The behaviour of this interface has already been documented in other articles (see above), but here's a brief description.

The IDisposable interface has one method—Dispose(). This method is not called by the garbage collector on destruction of an object. Its purpose is to provide a single interface in which code can force an instance of a class to mark its resources for garbage collection. Any resources that can be released immediately (for example, native handles or memory) are also to be freed.

For instance, all the GDI objects (System.Drawing.Graphics, System.Drawing.Pen, and so forth) implement IDisposable and in MSDN it's common for example code to explicitly call the Dispose() method after creating instances of these classes. GDI objects are a limited resource, and so should be released as soon as possible to prevent the system from running out of them. Hence, the inheritance from IDisposable.

The 'using' Keyword

Any class instance defined in the parameter list of a 'using' keyword has its Dispose() member called on exit of the following code block. For instance:

static void Function(Control control)
{
   Graphics g = control.CreateGraphics();
   Pen pen = new Pen(Color.Black);

   using (g, pen)
   {
      // ....
   }  // g and pen have their Dispose() methods called on exit of
      // the code block.

   // creation of an instance in the using keyword's parameter list
   using (Graphics graphics = control.CreateGraphics())
   {
      // ....
   }  // graphics has its Dispose() method called on exit of the
      // code block.
}

This gives us a method for ensuring that post-conditions of code blocks are always met. If the code is enclosed in a 'using' block, we know that any classes that are included in the parameter list will always have their Dispose() method called on exit from that block.

This includes the situation where an exception is thrown inside of the code block. Therefore, by using the above C++ example, we can implement in C# in the following manner.

public unsafe class SetBoolean : IDisposable
{
   bool *m_pfBoolean = null;
   bool m_fValue = false;

   public SetBoolean(bool *pfBoolean, bool fValue)
   {
      m_pfBoolean = pfBoolean;
      m_fValue = *pfBoolean;
      *pfBoolean = fValue;
   }

   public void Dispose()
   {
      *m_pfBoolean = m_fValue;
   }
}

public class Example
{
   private bool m_fValue = false;

   public unsafe bool Function()
   {
      fixed (bool *pfValue = &m_fValue)
      {
         using (SetBoolean setBoolean = new SetBoolean(pfValue,
                true))
         {
            if (!Function1())
            {
               return false;
            }

            if (!Function2())
            {
               return false;
            }

            if (!Function3())
            {
               return false;
            }

            return true;
         }
      }
   }

   private bool Function1()
   {
      // do something
      return true;
   }

   private bool Function2()
   {
      // do something else
      return true;
   }

   private bool Function3()
   {
      // do another thing
      return true;
   }
}

'Using' the IDisposable Interface

The 'using' block ensures that SetBoolean.Dispose() is called on exiting from the block, which in turn returns the boolean m_fValue to its original state. Unfortunately, because we're dealing with pointers, we need to use the unsafe context. You also can do the following; it doesn't use unsafe contexts and pointers, but requires a private class to be defined for the Example class:

public class Example
{
   private bool m_fValue = false;

   public bool Function()
   {
      using (SetBooleanValue setBoolean =
             new SetBooleanValue(this, true))
      {
         if (!Function1())
         {
         return false;
         }

         if (!Function2())
         {
            return false;
         }

         if (!Function3())
         {
            return false;
         }

         return true;
      }
   }

   private bool Function1()
   {
      // do something
      return true;
   }

   private bool Function2()
   {
      // do something else
      return true;
   }

   private bool Function3()
   {
      // do another thing
      return true;
   }

   private class SetBooleanValue : IDisposable
   {
      private Example m_example = null;
      private bool m_fOldValue = false;

      public SetBooleanValue(Example example, bool fNewValue)
      {
         m_fOldValue = example.m_fValue;
         example.m_fValue = fNewValue;
         m_example = example;
      }

      public void Dispose()
      {
         m_example.m_fValue = m_fOldValue;
      }
   }
}

It must be noted that one of the advantages of using this method is that if an exception is thrown inside of the 'using' block, the variable will always be returned to its original state, thus allowing applications to safely recover from known exceptions.

Example

The following is a class that sets the cursor to the wait cursor on construction, and returns the cursor afterwards to its original shape through its Dispose() method.

using System.Windows.Forms;

public class WaitCursor : IDisposable
{
   static private Cursor m_cursorOld = null;
   static private int m_nReferenceCount = 0;

   static private void ShowWait()
   {
      if (m_nReferenceCount == 0)
      {
         m_cursorOld = Cursor.Current;
         Cursor.Current = Cursors.WaitCursor;
      }

      m_nReferenceCount += 1;
   }

   static private void PopWait()
   {
      if (m_nReferenceCount > 0)
      {
         m_nReferenceCount -= 1;

         if (m_nReferenceCount == 0)
         {
            Cursor.Current = m_cursorOld;
            m_cursorOld = null;
         }
      }
   }

   public WaitCursor()
   {
      ShowWait();
   }

   public void Dispose()
   {
      PopWait();
   }
}

An example of use would therefore be:

public class ExampleWait
{
   static public void DoSomething()
   {
      using (WaitCursor waitCursor = new WaitCursor())
      {
         // do something which takes a while
         for (int nIndex = 0; nIndex < 1000000; nIndex++)
         {
         }
      }

      // cursor is returned to normal on exit of the 'using' block
   }
}

Conclusions

We have seen how the IDisposable interface can be used for much more than just the releasing of memory and resources. We have seen how it can be used to ensure that post conditions on exiting from code blocks (even when exceptions are thrown) are always met which leads to more reliable and robust code.



About the Author

David McClarnon

He first encountered Windows programming using Visual C++/MFC version 1.5 on Windows 3.11 a very long time ago. He is now a contract developer specialising in .NET/native interop with p/invoke.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

Most Popular Programming Stories

More for Developers

RSS Feeds