Introduction
The draw resource classes found inside the System.Drawing
namespace (Pen
, SolidBrush
, ...) are actually wrapping the underling native Windows GDI resources. Like all system resources, their amount is limited, and so they should be returned to the Operating System as soon as possible.
That's why .NET documentation tells you to call the Dispose()
method on Pen
s, Brush
es etc., as soon as they are not used anymore.
Background
But .NET is a managed environment and has a garbage collector, then why do we have to manually release resources? Consider the following usual paint code:
protected override void OnPaint(PaintEventArgs e)
{
Pen myPen1 = new Pen(Color.Black, 3);
Pen myPen2 = new Pen(Color.Lime, 3);
SolidBrush myBrush1 = new SolidBrush(Color.Red);
SolidBrush myBrush2 = new SolidBrush(Color.Lime);
}
Pen
and SolidBrush
are internally holding unmanaged native Win32 GDI resources (HBRUSH
, HPEN
etc.). These are precious Windows system resources and should be released as soon as possible. When OnPaint()
exits, the only references to myPenX
and myBrushX
are gone, so the .NET garbage collector will release the instances sometime later. This will, of course, also release the underlying native handles. But the point is, sometime later. For the time span between the last reference gone and the garbage collector releasing the unused instances, the underlying native resources are occupied although not in use anymore. This can be a problem on heavy system load situations. And, like all resources, we should use them not more than necessary, and return them as soon as we do not need them anymore.
So, we have to call Dispose()
on all Pen
s, Brush
es, etc.:
protected override void OnPaint(PaintEventArgs e)
{
Pen myPen1 = new Pen(Color.Black, 3);
Pen myPen2 = new Pen(Color.Lime, 3);
SolidBrush myBrush1 = new SolidBrush(Color.Red);
SolidBrush myBrush2 = new SolidBrush(Color.Lime);
myPen1.Dispose();
myPen2.Dispose();
myBrush1.Dispose();
myBrush2.Dispose();
}
If you have a complicated drawing routine incorporating many pens and brushes, calling Dispose()
on all instances can be a tedious and error prone task. You easily add a new brush in the middle of your code and forget to call Dispose()
on it before method exit. And be aware: if an exception is thrown somewhere in your drawing code, the control flow can jump out of OnPaint()
without running to its end. So, the Dispose()
of your instances will not be called at all.
C# provides the using
statement for this situation:
protected override void OnPaint(PaintEventArgs e)
{
using (Pen myPen1 = new Pen(Color.Black, 3))
using (Pen myPen2 = new Pen(Color.Lime, 3))
using (SolidBrush myBrush1 = new SolidBrush(Color.Red))
using (SolidBrush myBrush2 = new SolidBrush(Color.Lime))
{
}
}
This solves the problem perfectly for simple OnPaint()
methods. But if there is more complicated painting involving different paint resources depending on the control's state, we have to create all possibly used resources before painting:
protected override void OnPaint(PaintEventArgs e)
{
using (Pen myPen1 = new Pen(Color.Black, 3))
using (Pen myPen2 = new Pen(Color.Lime, 3))
using (SolidBrush myBrush1 = new SolidBrush(Color.Red))
using (SolidBrush myBrush2 = new SolidBrush(Color.Lime))
{
if ( some condition )
{
if ( some other condition )
{
}
}
else
{
}
}
}
Of course, this is not a big deal if only one brush or pen is unnecessarily created. I do visualizations for industrial processes, and there we use sophisticated user controls involving a lot of complicated painting using many different resources. And, there are two big 1600x1080 screens showing many of those controls on the same time. All the needless draw resources created have a measurable impact on the system performance. To avoid this, we would end with something like this:
protected override void OnPaint(PaintEventArgs e)
{
using (Pen myPen1 = new Pen(Color.Black, 3))
using (SolidBrush myBrush1 = new SolidBrush(Color.Red))
{
if ( some condition )
{
using (SolidBrush myBrush2 = new SolidBrush(Color.Lime))
{
if ( some other condition )
{
using (Pen myPen2 = new Pen(Color.Lime, 3))
{
}
}
}
}
else
{
}
}
}
So far, so good. But imagine the code we are ending up with if there are 6 conditions and 12 draw resources, some of them used in all control paths, some in only one, some in three of them, and so on. The resulting construct of nested using
statements is hard to read and even harder to maintain. If the drawing code changes and a resource used before only in one control path is newly used in another control path too, we have to rearrange the whole code.
Helper class to dispose drawing resources on method exit
I have a strong C++ background, and in C++, it is a best practice to encapsulate resources in objects to get them released as soon as they go out of scope. This is especially important to make code exception safe. So, I ported the C++ approach to C#: have a helper class releasing resources.
using System;
using System.Collections.Generic;
class Disposer : IDisposable
{
private List<IDisposable> m_disposableList =
new List<IDisposable>();
private bool m_bDisposed = false;
public Disposer()
{
}
public void Add(IDisposable disposable)
{
if (m_bDisposed)
{
throw new InvalidOperationException(
"Disposer: tried to add items after call to Disposer.Dispose()");
}
m_disposableList.Add(disposable);
}
#region IDisposable members
public void Dispose()
{
if (!m_bDisposed)
{
foreach (IDisposable disposable in m_disposableList)
{
disposable.Dispose();
}
m_disposableList.Clear();
m_bDisposed = true;
}
}
#endregion
}
All drawing resource classes in the System.Drawing
namespace implement the IDisposable
interface. It declares a method, Dispose()
. For details, see the MSDN documentation on IDisposable
. We add all object instances to be disposed to an instance of Disposer
, which iterates the internal list and calls Dispose()
on all entries. Be aware that this code is not thread safe. For this, m_disposableList
and m_bDisposed
would need to be protected with a lock()
. The intended use case for this helper class is inside user controls, where all members have to be accessed from the thread which initially created the control anyway.
Usage of the Disposer helper class
Now, the OnPaint()
method is changed to use the Disposer
helper class:
protected override void OnPaint(PaintEventArgs e)
{
using (Disposer drawResDisposer = new Disposer())
{
Pen myPen1 = new Pen(Color.Black, 3);
drawResDisposer.Add(myPen1);
SolidBrush myBrush1 = new SolidBrush(Color.Red);
drawResDisposer.Add(myBrush1);
if ( some condition )
{
SolidBrush myBrush2 = new SolidBrush(Color.Red);
drawResDisposer.Add(myBrush2);
if ( some other condition )
{
Pen myPen2 = new Pen(Color.Yellow, 3);
drawResDisposer.Add(myPen2);
}
}
else
{
}
}
Just add every instantiated drawing object to the Disposer
, drawResDisposer
, and on exit of the code block surrounded by using {}
, drawResDisposer.Dispose()
is called automagically, which calls the Dispose()
of all the instances added to it. No need to know which of the draw resources where actually needed and created. Just add them to the disposer, use them, and forget about them. I think this is a much cleaner and easier to maintain code structure.
The Disposer
class can also be used for every other class encapsulating native and unmanaged resources. As long as the class implements the IDisposable
interface.
I am a fan of one single kind of programmer laziness only: Don't do things manually which can be done by the compiler or the runtime automatically.
Improving the Disposer with WeakReference
In advanced user controls, we should not create all drawing resources on each call of OnPaint()
. Painting occurs quite frequently in situations like resizing. It is a waste of resources to create a pen every time it is used and then hand it over to the garbage collector for destruction directly. Each drawing resource should be created on first use, and disposed inside the control's Dispose()
method. In other words, we create a singleton for each different drawing resource type used for painting. This way, the resources are held by the control during its lifetime, but this is still better than constructing and destroying the same drawing resources over and over again. But, this approach makes the problem even worse: the resources are now created and destroyed in different methods of the control class, so it is much harder to get the Dispose()
of each resource called inside the control's Dispose()
method. If we fail to dispose the disposer instance when the control gets destroyed, the internally existing references to the IDisposable
instances stop the garbage collector from freeing them. Less than better...
To make the disposer helper class more useful in such situations, it is changed to use the .NET System.WeakReference
class. It allows the garbage collector to free an object although there is still a reference, a weak one. See the MSDN documentation for details. This class is made for such kind of situations: a reference to an object which can be used to dispose it but does not count itself as a reference for the garbage collector.
using System;
using System.Collections.Generic;
class Disposer : IDisposable
{
private List<WeakReference> m_disposableList =
new List<WeakReference>();
private bool m_bDisposed = false;
public Disposer()
{
}
public void Add(IDisposable disposable)
{
if (m_bDisposed)
{
throw new InvalidOperationException(
"Disposer: tried to add items after call to Disposer.Dispose()");
}
m_disposableList.Add(new WeakReference(disposable));
}
#region IDisposable members
public void Dispose()
{
if (!m_bDisposed)
{
foreach (WeakReference weakRef in m_disposableList)
{
try
{
if (weakRef.IsAlive)
{
IDisposable strongRef = (IDisposable)weakRef.Target;
if (strongRef != null)
{
strongRef.Dispose();
}
}
}
catch (System.InvalidOperationException ex)
{
}
}
}
m_disposableList.Clear();
m_bDisposed = true;
}
#endregion
}
Here is the usage of the improved class inside a control:
class MyControl : UserControl
{
private Disposer m_Disposer = new Disposer();
private Pen m_Pen1 = null;
private Pen1
{
get
{
if (m_Pen1 == null)
{
m_Pen1 = new Pen(Color.Lime, 5);
m_Disposer.Add(m_Pen1);
}
return m_Pen1;
}
}
private SolidBrush m_Brush1 = null;
private Brush1
{
get
{
if (m_Brush1 == null)
{
m_Brush1 = new SolidBrush(Color.Red);
m_Disposer.Add(m_Brush1);
}
return m_Brush1;
}
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawLine(this.Pen1, point1, point2);
e.Graphics.FillRectangle(this.Brush1, rect);
}
#region IDisposable members
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
m_Disposer.Dispose()
base.Dispose(disposing);
}
}
#endregion
}
All you have to is add the lines shown in bold. If you forget the call to m_Disposer.Dispose()
, the garbage collector will take care of the draw resources. The references held inside the disposer class do not count for the garbage collector anymore. This is exactly what we want.
Points of interest
This possible usage of the using
keyword is often overseen:
using (myType myVar = new myType())
{
}
If the code block following using()
is left on any path, including an exception, a return
in the middle etc., myVar.Dispose()
is called implicitly. The needed calls are inserted by the C# compiler. The only restriction for types used in using()
is that they implement IDisposable
.
The using
statement is actually just syntactic sugar which is extended by the C# compiler to the following code. You can see this if you inspect your binary assembly with Reflector:
try
{
myType myVar = new myType();
}
catch (System.Exception ex)
{
}
finally
{
myVar.Dispose();
}
Conclusion
Although no resources will really be leaked by not calling Dispose()
on GDI wrapper classes, it's a good habit to do so. This way, they are released when they are not needed anymore, and not sometime later when the garbage collector is run the next time.
Better idea?
I'm aware of the fact that this is a C++ approach. I'm relatively new to C#, but did C and C++ since more than a decade before. So, if you have a more "C#-ish" way to do this, please tell me.
History
- 07/07/2009 - Initial version.
- 10/13/2009 - WeakReference version added, some minor text additions.
- 10/22/2009 - Reworked code samples to better show the intended use case.