When I started out with .NET, I wasn’t quite happy with the documentation provided
for the IDisposable
interface by Microsoft. The examples always
depicted some class using an unmanaged resource that needed to be evicted from
memory. To achieve this, said class implemented IDisposable
, a
finalizer and a Dispose(bool disposing)
that was called from both
the finalizer and the IDisposable.Dispose()
method.
But it didn’t tell me that you don’t need the whole disposable pattern with a
finalizer and Dispose(bool disposing)
method unless your class
actually owns an unmanaged handle directly. Or that adding a finalizer to a
class means each instance of it is tracked in a finalization queue, which means
more work for the garbage collector.
This article will introduce you to the disposable pattern the way I would have wished for when I switched to .NET!
Why Dispose?
.NET is a runtime environment based on garbage collection. That means instead of allocating memory, using it and then freeing it again, you can just allocate away and when your program has no more references to the allocated memory, .NET will free it for you automatically.
This is done by the garbage collector. It will run every now and then, check which objects your application is no longer referencing (so called “garbage”) and reclaim the resources used by those objects.
This works great for memory, but what if the object in question is a file? Open the file, write to it and then… just let it go. It will eventually be closed — but you don’t know when the garbage collector will come around to do it. So the file stays locked until this happens. Not good if you’re writing an image editor where the user might want to use the image file after he saved it.
By explicitly disposing objects, you can tell .NET that you want a resource freed right here and now.
IDisposable
The IDisposable
interface is just an interface with a single method
named Dispose()
, nothing more. It has no special meaning for the
.NET runtime and implementing it will not change your object’s behavior at all.
All it’s good for is to provide a standardized way for you to tell an object to
release its resources. The C# language even provides some built-in support for
IDisposable
. Instead of writing:
IDisposable disposableObject = createSomething();
try {
DoSomethingWhichMightThrow();
}
finally {
disposableObject.Dispose();
}
C# lets you write:
using(IDisposable disposableObject = createSomething()) {
DoSomethingThatMightFail();
}
As stated before, this is a pure C# language feature that will be rewritten to something resembling the former code example when it is compiled to IL code.
Finalizers
Finalizers are special methods that get called when an object is collected by
the garbage collector. They can be seen as an emergency path to release unmanaged
resources if the user of an object forgot to call IDisposable.Dispose()
on it.
Adding a finalizer to a class means that whenever you create an instance of said class, that instance will be added to a global finalization queue that tells the garbage collector not to reclaim the object’s memory until its finalizer has been executed. This of course means additional management overhead.
That’s why you often see this little trick in classes with a finalizer:
class MyClass : IDisposable {
/// <summary>Finalizer that will be called by the GC</summary>
~MyClass() {
ReleaseUnmanagedResources();
}
/// <summary>Immediately releases all resources owned by the object.</summary>
public void Dispose() {
ReleaseManagedResources();
ReleaseUnmanagedResources();
// We have released our resources already. Tell the GC that it
// doesn't need to call the finalizer anymore.
GC.SuppressFinalize(this);
}
}
GC.SuppressFinalize()
removes the instance from the finalization
queue, so the garbage collector can now reclaim the object without waiting for
its finalizer to be executed.
Simplified Disposable Pattern
You can support the disposable pattern to two different degrees. This simplified variant is the one you should prefer as long as your class is not the direct owner of an unmanaged resource.
Use it whenever you need to call Close()
on a FileStream
,
unregister from an event or when your class owns another object that implements
IDisposable
(which might even be an object that wraps an unmanaged
resource itself and requires the full disposable pattern)
You can implement IDisposable
in a very simple manner by only adding
IDisposable
to the list of interfaces for your class and writing
the finalization code directly into the Dispose()
body:
class MyClass : IDisposable {
/// <summary>Immediately releases all resources owned by the object</summary>
public void Dispose() {
// Perform object finalization here
}
}
Example
Let’s take an example from my favorite topic, game programming, and check out
how IDisposable
could be used in a real-world scenario that doesn’t
have anything to do with unmanaged resources!
Let’s see:
- The
Game
class can load levels asynchronously -
While it is loading,
Game
regularly triggers theLoadingProgressUpdated
event to notify its subscribers about the achieved progress -
The
LoadingScreen
class subscribes to theGame
‘sLoadingProgressUpdated
event
Looks like the perfect construction kit for a game with a loading screen!
Now what if the level has been loaded and the loading screen is no longer needed? Thanks to the gargabe collector we could just drop it, right?
Right. Except that the garbage collector will not reclaim the
LoadingScreen
because it’s still subscribed to the
Game.LoadingProgressUpdated
event (in other words, the
Game
class still has a reference to it).
And that, when the player finishes the level and we set up another loading screen
for the next level being loaded, it will still be registered. In level 10, the
Game
class will already have 10 LoadingScreen
s subscribed
to it. Oops.
That’s where we could use IDisposable
: Let the
LoadingScreen
implement IDisposable and unsubscribe from the
event when it’s disposed. Then take care to call LoadingScreen.Dispose
after a level has finished loading and everything is fine.
Full Disposable Pattern
This 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 in the specific case
where a class directly owns an unamaneged resource handle.
class MyClass : IDisposable {
/// <summary>Finalizer that will be called by the GC.</summary>
~MyClass() {
Dispose(false); // false == called by the GC
}
/// <summary>Immediately releases all resources owned by the object.</summary>
public void Dispose() {
Dispose(true); // true == called explicitly by user code
// We have released our resources 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="calledExplicitly">
/// If true, the object is being disposed explicitly and can still access
/// other managed objects it is referencing. If false, other managed objects
/// maybe have been destroyed already and mustn't be touched.
/// </param>
protected virtual void Dispose(bool calledExplicitly) {
if(calledExplicitly) {
// Perform finalization of managed objects here
}
// Perform finalization of unmanaged objects here
}
}
As you might have noticed, I took the liberty of renaming the disposing
parameter you will find in the MSDN example to calledExplicitly
.
That’s because having a parameter named disposing
in a method named
Dispose()
doesn’t exactly light a candle to me ;)
The important thing to note here is that managed resources will only be disposed
if the Dispose(bool calledExplicitly)
method is invoked by an
explicit call through IDisposable.Dispose()
. This is important because,
as mentioned before, if the method is being executed through the finalizer,
it must not access any other objects, not even to call Dispose()
on them – they may have already been destroyed.
Example
Here’s an example demonstrating the use of the full disposable pattern with a finalizer and everything:
What do we have here?
-
A
DatabaseConnection
class that communicates with some proprietary database engine through a “pipe” -
Said “pipe” is an unmanaged resource that needs to be closed, so the
DatabaseConnection
class implements the full disposable pattern. -
A
DatabaseWriter
class the owns aDatabaseConnection
instance. It implements the lightweight disposable pattern.
Without the finalizer (DatabaseConnection.~DatabaseConnection()
),
if the programmer forgot to call Dispose()
on his
DatabaseConnection
, the pipe would never be closed since the garbage
collector doesn’t know what to do with the IntPtr
.
But with the finalizer in place, the garbage collector will call it and be able to close the pipe even if the programmer forgot to do so explicitly.
The DatabaseWriter
class doesn’t have a finalizer. It couldn’t do
anything in it anyway – calling DatabaseConnection.Dispose()
would
be risky because the DatabaseConnection
could already be destroyed
at this point.
So DatabaseWriter
uses the lightweight dispose pattern and disposes
the DatabaseConnection
only if it is explicitly called.