Implementing IDisposable Properly
The Confusion of IDisposable
Maybe it's just me but I have a hard time figuring out the best way to implement IDisposable, let alone when to use the interface. I understand the basics, that you use IDisposable to manage releasing managed and unmanaged resources. But why do you need to use it?
Pluralsight's IDisposable Best Practices for C# Developers by Elton Stoneman
You do have to have a Pluralsight account in order to watch this course but it was worth it to me.
Note: Pluralsight offers a 10 day free trial on their website. You can also get a free 3 month trial to Pluralsight by joining the free Visual Studio Dev Essentials program. The program has a lot of other features as well, such as the GIT compatible source control management for up to 5 developers!
The Nitty and the Gritty
Elton outlines 6 best practice guidelines in his course and takes you through a few demos that show you issues you can encounter when you don't use IDisposable while accessing unmanaged resources and how to resolve them. He has valuable tips regarding troubleshooting issues that can arise when you decide you don't need no stinkin' IDisposable or if you've implemented it improperly.
I'm not going to duplicate all of his work since his course is over 2 hours long, but I will point out some of the highlights...
What is IDisposable for
The primary use of this interface is to release unmanaged resources. The garbage collector automatically releases the memory allocated to a managed object when that object is no longer used. However, it is not possible to predict when garbage collection will occur. Furthermore, the garbage collector has no knowledge of unmanaged resources such as window handles, or open files and streams.
Use the Dispose method of this interface to explicitly release unmanaged resources in conjunction with the garbage collector. The consumer of an object can call this method when the object is no longer needed.1
Use the Dispose method of this interface to explicitly release unmanaged resources in conjunction with the garbage collector. The consumer of an object can call this method when the object is no longer needed.1
When should you use IDisposable
Implement IDisposable only if you are using unmanaged resources directly. If your app simply uses an object that implements IDisposable, don't provide an IDisposable implementation. Instead, you should call the [child] object's IDisposable.Dispose implementation when you are finished using it.1
For the majority of the objects that your app creates, you can rely on the .NET Framework's garbage collector to handle memory management. However, when you create objects that include unmanaged resources, you must explicitly release those resources when you finish using them in your app. The most common types of unmanaged resource are objects that wrap operating system resources, such as files, windows, network connections, or database connections. Although the garbage collector is able to track the lifetime of an object that encapsulates an unmanaged resource, it doesn't know how to release and clean up the unmanaged resource.
2
If you write code that connects to a file, network resource, or database, you are using unmanaged code. Elton points out that a lot of people think that if they're calling some .Net objects such as a System.Data.Common.DbConnection, that you are using managed code. According to the Microsoft documentation quoted above, if you're using a database connection you cross into the unmanaged resource realm. And if you do that you should implement IDisposable.What is the IDisposable interface definition?
Referring to the Microsoft IDisposable Interface documentation, the interface has a single method "Dispose()". That doesn't seem like it should be too hard to implement...How should you implement IDisposable?
I implemented the IDisposable interface on a database object providing a definition for the single Dispose() method. However, when I compiled the project I got compilation warnings that my implementation wasn't correct.Here's the basic thing about that thing...
The consumer of an object implementing IDisposable should call the Dispose() method when the resource is no longer needed. You can do this by wrapping the call to the IDisposable object in a "using" construct (a language construct, not the using namespace directive) or by wrapping an explicit call to the Dispose() method in a try/catch block:
using( var usingObject = new ObjectImplementingIDisposable() ) {
// Code accessing usingObject.
}
or
try {
var usingObject = new ObjectImplementingIDisposable();
// Code accessing usingObject.
}
finally {
if( usingObject != null )
usingObject.Dispose();
}
There is a problem with the above code, however. What if the client fails to use either of the above techniques? In the case of a database connection, for example (and the Pluralsight course demonstrates this before your very eyes), you end up with orphaned resources that the Garbage collector won't clean up and which can cause database timeouts not just for your application but for any application trying to access the database.
To handle cases in which they fail to call Dispose, you should either use a class derived from SafeHandle to wrap the unmanaged resources, or you should override the Object.Finalize method for a reference type.1
Adding a Finalizer
I'll cover adding a Finalizer. First, the convention is to refactor the Dispose() method so that it calls a virtual method that can be over-ridden by any derived class, such as:// Tracks whether this object has been disposed
// to prevent multiple attempts at disposing.
private bool _disposed;
protected virtual void Dispose( bool disposing )
{
if( _disposed )
{
// Exit the method if we've already disposed of the resources.
return;
}
// Release managed child resources if applicable.
if( disposing )
{
// Release the _sqlConnection resources if they still exist.
if( _sqlConnection != null )
{
_sqlConnection.Dispose();
// Setting this to null explicitly is to ensure that
// if this method is called again the test for
// ( _sqlConnection != null ) will work as desired.
_sqlConnection = null;
}
// Set the member flag indicating that the dispose call has been performed.
_disposed = true;
}
// Always release any unmanaged child resources here.
// base.Dispose( disposing );
}
The IDisposable.Dispose() method is then refactored to call the Dispose( bool disposing ) method and to notify the GC (Garbage Collection) manager that this object does not need to be processed:
public void Dispose()
{
// Call the protected Dispose method to release resources.
Dispose( true );
// Let GC know this object has cleaned up it's own resources.
// This will prevent the GC from having to do any collection
// or calling the finalizer.
GC.SuppressFinalize( this );
}
// The GC will call this finalizer automatically but it is resource expensive.
// Using Dispose() properly is more efficient.
// Don't add this if the object does NOT use any unmanaged resources since it adds overhead.
~DBConnection()
{
// Call the Dispose method to release unmanaged resources.
// There should be no need to release the managed resources;
// they may have been released already.
Dispose( false );
}
Yes, there can be a bit more to this IDisposable than what I've listed here, for example if you inherit from a class that implements IDisposable in the manner describe here. But this is the basic way to implement IDisposable.
You can get my DBConnection.cs class that implements these techniques from my DropBox location at:
https://www.dropbox.com/s/xzplp2xy5uagxhx/DBConnection.zip?dl=0
-------
1 "IDisposable Interface." IDisposable Interface (System). Accessed June 03, 2017. https://msdn.microsoft.com/en-us/library/system.idisposable(v=vs.110).aspx.
2 "Cleaning Up Unmanaged Resources." Accessed June 03, 2017. https://msdn.microsoft.com/en-us/library/498928w2(v=vs.110).aspx.


Comments
Post a Comment