Yep. It's the return of the punny titles. Live with it.
This topic came up at work. Plenty has already been written about IDisposable, but that didn't stop me.
.net does not have deterministic resource management built in. In other words, by default, you have no idea when your resources will get reclaimed. If you open a file, it sits around, open, until the file object is garbage collection. You don't know when that will be. It should be obvious that this can cause problems.
Of course, for a file object, you can just add a Close method, and make sure you call it when you're done with the file. .net generalises this idea with the IDisposable interface. It defines a method, Dispose(), that tells the object to clear up whatever resources it has (the file gets closed).
But it's a convention - you have to call Dispose in your code. C# makes it easy with the "using()" statement. Any object created in the brackets of the using statement will have IDisposable.Dispose() called on it. at the end of the braces, even if an exception is thrown. We now have deterministic resource management. Hurrah!
So, when do you use it? The simple answer is any time a class implements IDisposable, you wrap it in a "using" statement. Don't try and roll your own - just use "using" (you'll probably forget to handle exceptions. And if you do, you've just implemented what using already does - try/finally).
And when do you implement it? If you handle any unmanaged resources (say you're using p/invoke to talk to an OS routine that handles resources, e.g. memory mapped files) you should implement IDisposable and clear those resources as soon as you can. If your class holds member level references to other classes that implement IDisposable, you should implement it and call Dispose on these references.
But what about types such as MemoryStream? It's not dealing with unmanaged resources - only memory. It implements IDisposable because it's base class implements it. Obviously FileStream needs to implement it because it's dealing with a file resource. Again, the answer is simple - memory is a resource also. If you don't dispose of a MemoryStream, that resource is sitting around until the garbage collector kicks in. This adds memory pressure and can very easily make it look like your app is leaking. Which is never a good thing, especially if you're building server side stuff.
On a similar note, it's probably a good idea to try and avoid keeping IDisposable objects as member fields - they stay alive for the lifetime of the object, and so does that resource pressure. You really want to try and keep IDisposable objects alive for as short a time as possible - this is where "using" is really useful. And don't hand them out as properties of your class - who owns the lifetime of the object now? You really don't want to call Dispose on an object that someone else is trying to use...
How do you implement it? I'm going to point you straight to the source. Here is Microsoft's recommended pattern. Read it and understand it - it's not terribly obvious at first read, and shows that this kind of deterministic cleanup should really have been more of a platform (or at least a more tightly integrated compiler) feature - this interface is not really pit of success territory. (Incidentally, the second version of managed C++ gets it right.)
And of course, it's complicated by the Finalizer. This is a method that's very much like Dispose, but it's what allows you to clean up unmanaged resources when someone forgets to call Dispose. If your object gets garbage collected and Dispose hasn't been called, you get added to a list and a background thread will eventually call your Finalizer. You can only clean up unmanaged resources here - any other unmanaged object you have a reference to might already have been garbage collected. Finalizing is very costly - you don't want objects to get onto the finalizer queue. Always use "using"!
This post brought to you by the word "deterministic" and the American spelling of finalise.