C# AutoCleanup class: Use IDisposable for cleaner code

July 5, 2012 by Wadeware in Software Development

Here is a common pattern of code:

// set some temporary state for the following block
try
{
// do some stuff
}
finally
{
// un-set the temporary state set above
}

A perfect example shows the wait cursor during an operation in a Windows Forms application:

this.UseWaitCursor = true;
try
{
// do some stuff
}
finally
{
this.UseWaitCursor = false;
}

Garbage Collection

For many years, every time I wrote one of these blocks of code I thought there must be a better way to accomplish this. There had to be something akin to the old CAutoPtr template class in C++/ATL. CAutoPtr class works because C++ supports deterministic calling of destructors. In other words, when an object is freed, its destructor is called immediately. This is in contrast to .NET managed code, which is garbage collected. Let me explain.

Code In managed code (C#, VisualBasic.NET, etc.), when a reference variable goes out of scope or the last reference to it is set to null (Nothing in Visual Basic), the object’s memory is not immediately released, nor is the destructor called right away. Instead, the object remains as-is until the .NET runtime needs a contiguous block of memory larger than the largest free block available on the heap. At this point, the runtime garbage collects heap memory. To simplify (in reality GC is quite a bit more complicated), the garbage collector finds all objects with no references to them and calls their destructors if they exist. It then frees the memory blocks used by those objects, and finally moves all of the remaining allocated blocks to one end of the heap, thus leaving all unallocated blocks in one large, contiguous segment. In today’s systems with 4 GB of RAM or more, garbage collection rarely fires because the free space is typically sufficient. Therefore, destructors are not called until the application’s process ends.

The IDisposable Interface

To allow developers a way to explicitly force an object’s destructor to be called immediately, the creators of the .NET Framework provided the IDisposable interface. The following are a few facts about IDisposable that will help you see how I utilize it to enable AutoCleanup :

  • IDisposable is an interface that you should implement in any class that manages critical, particularly unmanaged, resources.
  • IDisposable has one method, Dispose(), which should clean up any critical resources.
  • Unlike most interfaces, both C# and VB.NET have built-in knowledge and support of the IDisposable interface via the using statement (Using in VB.NET). When an object that implements IDisposable is wrapped in a using statement, its Dispose() method is automatically called immediately when the using statement goes out of scope. To illustrate, the following two methods are functionally identical:

public string ReadFileWithTryCatch()
{
string text;
StreamReader reader = null;
try
{
reader = new StreamReader(“c:\\foo.txt”);
text = reader.ReadToEnd();
}
finally
{
if (null != reader)
{
reader.Close();
}
}
return text;
}
public string ReadFileWithUsing()
{
string text;
// The StreamReader class implements IDisposable
using (var reader = new StreamReader(“c:\\foo.txt”))
{
text = reader.ReadToEnd();
}
// At this point, the using statement goes out of
// scope, which causes the StreamReader’s Dispose
// method to be called, which closes the file’s
// handle.
return text;
}

When I took this into consideration, I figured any auto-pointer style class would rely on the IDisposable interface to take advantage of the using statement, but I never took the time to work out the details. As I became more proficient with LINQ and lambda expressions, though, I realized that the combination of IDisposable and lambda expressions was what I needed.

Note: my AutoCleanup class does not internally make use of lambda expressions anywhere. Instead, it is when using AutoCleanup that lambda expressions (and optionally anonymous functions) are leveraged.

The Complete AutoCleanup Class

using System;

namespace WadewareLibrary.Utility
{
/// <summary>
/// Guarantees automatic execution of delegate functions
/// when an instance of this class is constructed and/or
/// when it is disposed.
/// </summary>
/// <remarks>
/// The <b>AutoCleanup</b> class is used in cases where
/// you would normally use a try/finally to ensure that
/// your cleanup code is executed, regardless of
/// whether an exception was thrown or not.  With the
/// <b>AutoCleanup</b> class, instead of all of the
/// try/catch lines which can make code less readable,
/// you can leverage the <see langword=”using”/>
/// statement, instantiating an instance of this class,
/// and pass either just the cleanup code or both the
/// constructor code and cleanup code to execute.
/// </remarks>
public class AutoCleanup : IDisposable
{
private bool disposed;
private readonly Action executeOnDispose;

/// <summary>
/// Constructs an <see cref=”AutoCleanup”/> object,
/// immediately executing a delegate function, and
/// then guaranteeing the execution of a second
/// deletate function when disposed.
/// </summary>
/// <param name=”executeOnConstruct”>
/// The delegate function to execute during
/// construction.
/// </param>
/// <param name=”executeOnDispose”>
/// The delegate function to execute when disposed.
/// </param>
public AutoCleanup(Action executeOnConstruct,
Action executeOnDispose)
{
if (null != executeOnConstruct)
{
executeOnConstruct();
}
this.executeOnDispose = executeOnDispose;
}

/// <summary>
/// Constructs an <see cref=”AutoCleanup”/> object,
/// guaranteeing the execution of a provided delegate
/// function when disposed.
/// </summary>
/// <param name=”executeOnDispose”>
/// The delegate function to execute when disposed.
/// </param>
public AutoCleanup(Action executeOnDispose)
{
this.executeOnDispose = executeOnDispose;
}

#region IDisposable Members
/// <summary>
/// Disposes the <see cref=”AutoCleanup”/> object,
/// executing the delegate function provided in the
/// constructor.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

//
// Internal implementation of the Dispose() method.
// See the MSDN documentation on the IDisposable
// interface for a detailed explanation of this
// pattern.
//
private void Dispose(bool disposing)
{
if (!this.disposed)
{
//
// When disposing is true, release all
// managed resources.
//
if (disposing)
{
if (null != this.executeOnDispose)
{
this.executeOnDispose();
}
}
disposed = true;
}
}
#endregion
}
}

One of the first things you will note in the class definition above is the use of a type named Action. Action is a basic delegate defined in the System namespace that takes no parameters and returns no value.  It is defined like this:

namespace System
{
/// <summary>
/// Encapsulates a method that has no parameters and does not return a value.
/// </summary>
public delegate void Action();
}

In other words, the Action delegate allows you to pass in a reference to a function to be executed when the time is right. In the case of the AutoCleanup class, there are two times when provided function references are executed: once when the class is constructed; and once when Dispose() is called. However, with lambda expressions, you no longer need to create a separate function to pass in, but instead can provide the necessary code in-line.

Instead, simply construct a new instance of it inside of a using statement, and pass in two parameter-less, anonymous functions.

Note: When an anonymous function has no parameters, on the left of the lambda expression operator (=>) where the parameter list would normally go, you must provide the open and close parenthesis with nothing between them.

private void Form1_Shown(object sender, EventArgs e)
{
using (new AutoCleanup(() => this.UseWaitCursor = true,
() => this.UseWaitCursor = false))
{
// do some stuff
}
}

Another Use for AutoCleanup

Furthermore, you can use AutoCleanup as a base class to create specialized derivations for common use cases. Below is an example that implements the UseWaitCursor logic.

using System;
using System.Windows.Forms;
namespace WadewareLibrary.Utility
{
/// <summary>
/// Automatically enables the wait cursor for a Windows
/// Forms control and disables it when the class is
/// disposed.
/// </summary>
public class AutoWaitCursor : AutoCleanup
{
public AutoWaitCursor(Control control)
: base(() => { control.UseWaitCursor = true;
Application.DoEvents(); },
() => { control.UseWaitCursor = false; })
{
}
}
}

To use this class all you need to do is:

private void Form1_Shown(object sender, EventArgs e)
{
using (new AutoWaitCursor(this))
{
// do some stuff
}
}

I find this far simpler and easier to read than the whole try/finally clumsiness. And its benefits are manifold.