There's plenty to say about the IDisposable interface and the using statement, but you probably don't have time to read it all (and I don't have time to write it all!), so I'm going to try to keep this short and simple.

First, let's make sure we're all on the same page. If you're not familiar with the relevant concepts, please take a moment to learn about .NET Garbage Collection, the IDisposable interface, the using statement, and the use of objects that implement IDisposable. (If you're a bibliophile, I understand that the book "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries" (ISBN 0321246756) contains additional material in section 10.3, "Dispose Pattern".)

Now that we're all familiar with the concepts, I'd like to call attention to a few things:

  • Implementation of the IDisposable interface by the author of a class is optional and completely unnecessary for a correctly written class. Even without IDisposable, the Garbage Collector will eventually clean up all of the class's resources (possibly with the help of a Finalize method override).
  • However, without IDisposable, there is no way of controlling *when* a class's resources will be cleaned up. The Garbage Collector (GC) runs only when it needs to, so it could be seconds, minutes, or even hours after you're done using a class before the GC runs and cleans up those resources. Any resources held by that class (like file handles, sockets, SQL connections, etc.) will remain in use until that class is cleaned up by the GC. This can cause unexpected problems when, for example, the user has closed a file in an application, but the application continues holding on to that file and prevents the user from copying it, moving it, etc.. Worse still, such problems will occur "randomly" according to whether or not the GC has run.
  • So it's a good practice to call IDisposable.Dispose whenever you're done using a class that implements IDisposable so that its resources can be freed immediately and deterministically (i.e., predictably). As a side effect, this helps minimize the resource consumption of your application which always makes users happy.
  • However, simply adding a call to Dispose at the end of a block of code isn't a complete solution because it means that Dispose won't get called if an Exception is thrown anywhere within that block of code. If you're going to call Dispose, you want to *always* call Dispose, so you really want the call to Dispose to be within the finally block of a try-finally statement pair.
  • To address this, you can manually add a try-finally pair, *or* you can use the using statement which does so for you! Under the covers, the using statement maps to a try-finally pair which calls Dispose for the specified object within the finally block, but the beauty of the using statement is that it's a simpler, more concise way of doing so that keeps all the relevant code in one place and hides the gory details from view. You can even declare and initialize the object in the using statement itself! (See the documentation for using for examples.)

With these points in mind, I propose following guidelines whenever dealing with an object that implements the IDisposable interface:

  • Always call the object's Dispose() method
  • Call Dispose() under all conditions (i.e., within a finally block)
  • Call Dispose() as soon as you're done with the object

Conveniently, the using statement makes it easy to do *all* of these things! The using statement is a simple programming construct that's very readable and that helps your code perform reliably, predictably, and efficiently. It doesn't get much better than that, so if you aren't already, please start using using today!