![]() |
Platforms, Frameworks & Libraries »
.NET Framework »
General
Intermediate
Simplified Resource Management with the DisposableCollection ClassBy Scott McMasterThis article demonstrates how a strongly typed collection of IDisposable objects can be used to simplify the management of multiple resources in .NET. |
C#, VB, Windows, .NET1.0, .NET1.1, .NET2.0VS.NET2003, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
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.
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)
'Do stuff with myPen
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) )
{
// Do stuff with myPen
}
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) )
{
// Do stuff with both pens
}
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) )
{
// COMPILE ERROR!
}
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) )
{
{
// Do stuff with a font and a brush
}
}
}
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.
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 IDisposables 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) );
// Do stuff with a font and a brush. They'll be disposed of by the
// DisposableCollection's Dispose method at the end.
}
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)
'Do stuff with 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.
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.)
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.
09/24/2004 - Initial release.
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 26 Sep 2004 Editor: Smitha Vijayan |
Copyright 2004 by Scott McMaster Everything else Copyright © CodeProject, 1999-2010 Web10 | Advertise on the Code Project |