Introduction
Properly cleaning up objects that reference unmanaged resources such as database connections and GDI handles is an essential part of bullet-proof .NET Framework programming. In this article, we will see how to use a disposable strongly typed collection of IDisposable
objects to simplify a common resource management scenario.
Background
Best .NET programming practices dictate that we should not wait for the garbage collector to dispose off managed objects that wrap unmanaged resources. These include GDI+ objects (which wrap Windows GDI resources, a notorious source of leaks in traditional Win32 programming) and database connections. Unless we explicitly dispose off such objects, it's theoretically possible that we will exhaust the system of the underlying resources before the finalizers of the managed objects have the chance to free them up. The IDisposable
interface is provided and implemented by these objects to facilitate their deterministic cleanup.
Unfortunately, in Visual Basic .NET, this often forces us to write annoying and ugly code such as the following:
Dim myPen As Pen
Try
myPen = new Pen(Color.Red)
Finally
If Not myPen Is Nothing Then
myPen.Dispose()
End If
End Try
In C#, the situation is made somewhat better by the presence of the using
statement, which opens a scope at the end of which a given object will be disposed, regardless of whether the scope is exited normally or via an exception. Thus, the previous VB.NET code cleans up to the following in C#:
using( Pen myPen = new Pen(Color.Red) )
{
}
Clearly, this removes a lot of our programming burden if we're using C#. Often, particularly in GDI+ code, you will find yourself working with multiple IDisposable
objects at the same time. If these objects are of the same type, you may put them all inside the same using
statement:
using( Pen myRedPen = new Pen(Color.Red),
myBluePen = new Pen(Color.Blue) )
{
}
When working with multiple IDisposable
objects, chances are they won't always be of the same type. For instance, you'll need a Font
and a Brush
if you want to use the Graphics.DrawString
method to paint some text. Unfortunately, here's where the using
statement starts to break down, because the following code generates a compile error:
using( Font myFont = new Font("Arial", 16),
SolidBrush myBrush = new SolidBrush(Color.Blue) )
{
}
That snippet doesn't compile because C# doesn't let you have more than one type in a single using
statement. So, your next alternative is to nest using
statements like this:
using( Font myFont = new Font("Arial", 16) )
{
using( SolidBrush myBrush = new SolidBrush(Color.Blue) )
{
{
}
}
}
But use more than a couple of levels of nesting, and your code gets ugly pretty quickly. This caused me to set out in search of a better solution.
Using the code
To provide for easier IDisposable
management in situations like these, my solution is the DisposableCollection
class. This class combines a very typical CollectionBase
implementation with a very typical IDisposable
implementation. By combining those two abstractions, you get a simpler programming model which will hopefully encourage you to be more diligent about ensuring your IDisposable
s are immediately disposed. In C#, the snippet using both a Pen
and a Brush
turns into this:
using( DisposableCollection dc = new DisposableCollection() )
{
SolidBrush myBrush = null;
Font myFont = null;
dc.Add( myBrush = new SolidBrush(Color.Red) );
dc.Add( myFont = new Font("Arial", 16) );
}
The DisposableCollection
's mission in life is to serve as a working repository (specifically, a strongly-typed collection) for IDisposable
objects that is itself disposable. Thus, you can insert it into a using
statement and know that whatever you add to it will be disposed when the DisposableCollection
's using
block terminates.
You can write similar code inside the Try
metaphor in VB:
Dim dc As New DisposableCollection
Try
myPen = new Pen(Color.Red)
dc.Add(myPen)
Finally
dc.Dispose()
End Try
Included are two solutions, one in C# and one in VB.NET, that implement some trivial form drawing, using the DisposableCollection
to supervise the necessary GDI+ objects.
Points of Interest
The DisposableCollection
class supports full collection semantics, so that you can, say, index in and remove items. However, I anticipate that the vast majority of all uses will only add items as in the above examples. It will throw an exception if you attempt to use any collection method after the DisposableCollection
has been disposed. It will also clear its internal list of IDisposable
objects, freeing them up to be garbage-collected. (Note that by that point, each collection item has already had its Dispose
method called.)
A Word About "Using" in the .NET Framework 2.0
As you can see, if you look at the publicly available prerelease documentation for Visual Studio .NET 2005 on MSDN, Visual Basic .NET is picking up the Using
statement. Moreover, multiple resources in the same Using
block need not be of the same type. Thus, the following code is valid:
Using f As New Font(FontFamily.GenericMonospace, _
12, FontStyle.Bold), b As New Pen(Color.Red)
e.Graphics.DrawLine(b, 5, 5, 25, 25)
e.Graphics.DrawString("Hello", f, Brushes.Blue, 20, 30)
End Using
This is a welcome improvement to the language that makes the DisposableCollection
obsolete in many scenarios. Unfortunately, as of the Beta 1 release of Visual Studio .NET 2005, the C# using
statement appears to have not changed and still requires all resources in a single using
block to be of the same type.
History
09/24/2004 - Initial release.
I have over 10 years of full-lifecycle software development experience on a variety of large projects. I have a B.S. in Math from the University of Nebraska-Lincoln, a Masters in Software Engineering from Seattle University, and a Masters in Computer Science from the University of Maryland. I specialize in building and installing tools, frameworks, and processes that improve developer productivity and product quality. I am currently based in the Seattle area.