We all know the Microsoft examples for IDisposable where some
kind of unmanaged resource (usually a Win32 handle of a database connection)
needs to be evicted in a timely manner and therefore needs to be manually
disposed. In this article, I will try to clear up some common misunderstandings
with the IDisposable pattern and explain exactly where it needs
to be implemented and how you can best make use of it.
What is IDisposable for?
In the most primitive sense, IDisposable is only an interface with
a single method (named Dispose()) that does absolutely nothing.
It came to life because there is no deterministic finalization in .NET, which
simply states that .NET doesn't give any guarantee on when an object's finalizer
will be called. Any object the programmer lets go of will stay in memory as it was until
the garbage collector comes around to collect and kill it.
Without this pattern, whenever you needed to ensure that a resource (like an
open file handle or, for our fellow game programmers, a graphics device resource)
is freed again, you'd have to implement some method like CloseFile()
or ReleaseTexture() that could be used to let go of these resources
immediately. IDisposable is merely a standardized fashion
to provide such a method to the user.
Their are basically two ways (or levels) you can choose when implementing
the IDisposable interface. Let's take a look at both now:
The Simple Implementation
You can implement IDisposable in a very simple manner by
only adding IDisposable to the list of interfaces implemented by
your class and writing the finalization code into the Dispose().
Here's an example of that:
/// <summary>Immediately releases all resources owned by the object.</summary>
public void Dispose() {
// Perform object finalization here
}
}
This implementation is straight-ahead and very lightweight. Unless you've got actual unmanaged resources to kill off, this should be your preferred implementation.
The Full Implementation
Here is the IDisposable implementation you will see demonstrated
in the MSDN docs and that a lot of developers have come to accept as the only
way to add finalization code to a class. It is only required for a specific case of
finalization, namely that of releasing directly used unmanaged resources.
/// <summary>Finalizer that will be called by the GC.</summary>
~MyClass() {
Dispose(false); // false == not being called by user code
}
/// <summary>Immediately releases all resources owned by the object.</summary>
public void Dispose() {
Dispose(true); // true == called by user code
// We are finalized already, tell the GC that it doesn't
// need to call the finalizer anymore
GC.SuppressFinalize(this);
}
/// <summary>Immediately releases all resources owned by the object.</summary>
/// <param name="calledByUser">
/// If true, the object is being disposed explicitely and can still access
/// the other managed objects it is referencing.
/// </param>
protected virtual void Dispose(bool calledByUser) {
if(calledByUser) {
// Perform finalization of managed objects here
}
// Perform finalization of unmanaged objects here
}
}
There are several things to note here. For once, there is an actual finalizer
involved in this implementation of the Dispose Pattern. In .NET, the
finalizer of an object is only ever invoked by the garbage collector. When
that happens, you don't know whether any other objects you're referencing
have already been destroyed. Thus, you mustn't access any managed objects
when the finalizer is invoked. As you can see, it calls the
Dispose() method with its parameter set to false.
The other important part is the IDisposable implementation.
It calls the Dispose() method with its parameter set to true
so it knows that other objects referenced by the instance can be accessed
without trouble. And then there's a line containing the statement
GC.SuppressFinalize(this);.
When you write a class that defines a finalizer, the GC will have to track all
instances you create of that class in its finalization queue. This makes
it a little bit harder for the GC to reclaim the memory because it has to wait
until the object's finalizer has run. With GC.SuppressFinalize(),
you tell the GC that you have just finalized the object yourself and that it doesn't
have to bother with calling the finalizer anymore.
As stated before, this pattern is only really of any use when your class is working
with unmanaged resources. And even then, the finalizer merely acts as
a safety net to guarantee that the unmanaged resources are released if you
forget to call Dispose() in your code.
Why Dispose Managed Objects?
The garbage collector will reclaim the memory used by your objects anyway, so
why should you ever need to implement a Dispose() method for
purely managed objects?
Reason 1

The most obvious case is when your object references other objects that implement
the IDisposable interface. Assume, for a moment, that there was a
DatabaseConnection class that required you to call its
Dispose() method to close the connection to some database again.
If you wrote your own DatabaseConnectionWrapper, you'd have to
implement IDisposable there, too (but only the lightweight flavor).
Reason 2

Your managed object registers itself to a long-living object in some way.
If, for example, you were going to write a computer game and had some
LevelManager class that provided an event named
LoadingProgressUpdated, there is a subtle problem. While a level
is loading, the user is presented with a nice loading screen, which, in terms of code,
is a class that renders a progress bar and subscribes to the
LoadingProgressUpdated event. Unless you explicitely dispose
the loading screen, it stays in memory until the LevelManager
is destroyed. Worse even, with each level you load, another instance might be added
to that event's subscription list -- almost a classical memory leak.
So whenever you need to clean up something, like unsubscribing from the events
of another class, reset some state or take down an unmanaged resource you use,
the lightweight IDisposable implementation is your friend.
IDisposable and the using Statement
If you're writing exception-safe code, you're probably used to having
try .. finally blocks in several places of your code:
MemoryStream memory = new MemoryStream(104857600); // 100 MB
try {
// Do something here
}
finally {
memory.Dispose();
}
}
The MemoryStream class implements IDisposable
and you're allocating a considerable amount of memory, so you dutifully want to
free that memory again as soon as possible, even when an exception is thrown.
C# has a little gimmick that allows you to shorten such cases where you want to
call Dispose() on a short-lived objects even when an exception occurs. The above
code, rewritten to use the using directive, looks like this:
using(
MemoryStream memory = new MemoryStream(104857600); // 100 MB
) {
// Do something here
}
}
Not only is this code shorter, it also guarantees that the variable for the short-lived
object (memory in this case) goes out of scope after it has been
disposed, completely eliminating the risk of accidentally using the disposed
MemoryStream in the code that follows.
"Unless you explicitely
"Unless you explicitely dispose the loading screen, it stays in memory until the LevelManager is destroyed"
Why is that? Why it stays in memory until level manager is destroyed? I cannot understand how these classes are related to each other... Is thi because LevelManaager created object of class LoadingScreen?
It's Because of the Event
When you subscribe to the event of another class, like to the
LevelManagerin my example, this means that theLevelManagerobtains a reference to your subscriber.Thus, the subscriber will stay in memory until either the
LevelManageris disposed (or until your class unsubscribes from theLevelManager).Post new comment