IDisposable tutorial
Recently at work we had to review our understanding of IDisposable. I wrote this little article to help me and everyone else get it nailed. For me, it took visualizing what happens at the time of an explicit call to Dispose() and what happens when a destructor is run. The difference between those two scenarios motivates and explains the code.
Beyond knowing that we might have something to cleanup, it's hard to understand why the mechanics of doing it appear so complex. The best way to understand it is to visualize the two very different contexts your cleanup code can be called in.
The first case, somebody called Dispose() on your object: This is easy to understand. Somebody has your object pointer, they make that explicit call, and then (hopefully) they set the pointer to null or let it fall out of scope. Only a nimrod would keep making calls on your object, right? In this case you get to do your cleanup. You can do anything you want, in fact! Do you want to send mail to somebody? No problem! Your object is alive, all of it's children objects are alive...go crazy!
The second case, the Finalizer Thread called your destructor: Here is where things go off the rails. You can't do anything you want. If you try to make calls on some of your member objects, you may find to your dismay that they've already been finalized, and won't respond the way you hoped. In effect, they are dead or dying. You are wandering around in a chaotic battlefield! The only thing you can do safely is release unmanaged resources. These are file handles or pointers to memory that the Garbage Collector knows nothing about. And you have to hurry. If you block the Finalizer Thread, you end up blocking the whole runtime. So in this case, you definitely have to restrict yourself. Here are the rules:
Why can't I touch those object member variables, you ask? The reason is that the finalizer thread operates on a list of objects ready for finalization. The objects are in that list in no particular order. The fact that your object refers to the child object carries no weight anymore. Once your object was marked for finalization, your objects opinion about what objects are important no longer matters. That is why it's dangerous to try to use any object member fields.
But I don't have a destructor, you say? Okay. In that case, when your object goes out of scope nothing will happen. Of course, if you actually have the responsibility to clean something up this is not so good...
Microsoft has established a convention to make these two different cases easy to distinguish in code. In a nutshell:
The "disposing" parameter is confusing. Sometimes, people write it as "finalizing" and reverse the true and false constants in the code above. Stick with disposing though, it seems to be winning the war of convention. Just remember: if disposing is true, then I came in here from an explicit call to Dispose(), and not from a finalizer.
I recommend to always create a protected virtual void Dispose(bool disposing) method in your class that implements dispose. Then, make your implementation of Dispose() look exactly like the code above. Make your destructor only do one thing...call Dispose(false).
This is purely syntactic sugar, but lots of folks like to do this:
If you follow this pattern you can forget about the Dispose() method alltogether. You can forget about remembering what to do if disposing is true or false. You just put your two kinds of cleanup code in the provided methods. We do this pretty often in our codebase.
Another enhancement is a disposed boolean variable. This ensures that cleanup is only done once. I think that if you set your fields to null, and do a null test on the way in then you don't need this, but perhaps I'm saving a penny to lose a pound. :-). Here is the code above modified to use a disposing variable. NOTE: this doesn't make the code any more thread safe. It just frees you from having to be careful in your cleanup code when it comes to null checks.
Stay with me man, breath! We are almost there. Now, for a simple class the code we have above is perfect, and absolutely correct. If you are doing a code review, and you can't map the dispose logic of the class one-for-one to the logic above, then there is a bug in that code.
But... what about class hierarchies? This is actually a big problem. Because if your understanding of IDisposable for the one class case is a bit fuzzy, it will really fall apart when you consider a class hierarchy. But if you really understand the pattern above, and the reasons behind it, then you'll make correct and logical decisions when faced with creating, altering or debugging the dispose logic in a class hierarchy.
In a class hierarchy, the first class (call it B) who chooses to implement the IDisposable interface has a very concrete responsibility:
Now, if you have class C, which derives from B, then you only have to do one thing in C to carry on the tradition of correct dispose behavior:
What if C doesn't have any special work to do for dispose? Neither managed nor unmanaged resources to clean up? In that case C doesn't have to implement Dispose(bool disposing). Yay.
Note the call to the base class Dispose(disposing) method. That is very important. Note that this call is at the end of the Dispose(disposing) method. This reflects that C's resources are created after B's resources, and therefore C's resources should be cleaned up before B's resources are.
One case where you should implement IDisposable but is not very obvious is when your member variables implement IDisposable. The problem is what if your member variable is a FileStream. Now, it's true, when your object disappears, eventually the finalizer will run and find the FileStream object, call it's destructor, which will call Dispose(false) and the unmanaged resources will ultimately be cleaned up. But it's kind of rude.
You see, the FileStream advertised that he is ready and willing to clean up the moment you snap your fingers and call Dispose() on him! But if you don't implement IDisposable yourself so that a user of your class (who can't see the FileStream) can take advantage of that behavior, well then...a baby cries somewhere. Very loudly.
Of course, if you make sure to clean up that member variable before your object falls out of scope (maybe you have a "Close()" method or some convention like that), then you can sleep well again. However, it's pretty easy to implement IDisposable, now that you know what's going on!
Why is it so complicated?
Beyond knowing that we might have something to cleanup, it's hard to understand why the mechanics of doing it appear so complex. The best way to understand it is to visualize the two very different contexts your cleanup code can be called in.
The first case, somebody called Dispose() on your object: This is easy to understand. Somebody has your object pointer, they make that explicit call, and then (hopefully) they set the pointer to null or let it fall out of scope. Only a nimrod would keep making calls on your object, right? In this case you get to do your cleanup. You can do anything you want, in fact! Do you want to send mail to somebody? No problem! Your object is alive, all of it's children objects are alive...go crazy!
The second case, the Finalizer Thread called your destructor: Here is where things go off the rails. You can't do anything you want. If you try to make calls on some of your member objects, you may find to your dismay that they've already been finalized, and won't respond the way you hoped. In effect, they are dead or dying. You are wandering around in a chaotic battlefield! The only thing you can do safely is release unmanaged resources. These are file handles or pointers to memory that the Garbage Collector knows nothing about. And you have to hurry. If you block the Finalizer Thread, you end up blocking the whole runtime. So in this case, you definitely have to restrict yourself. Here are the rules:
- Don't make blocking calls
- Don't examine/change/even-think-about member variables that are objects
Why can't I touch those object member variables, you ask? The reason is that the finalizer thread operates on a list of objects ready for finalization. The objects are in that list in no particular order. The fact that your object refers to the child object carries no weight anymore. Once your object was marked for finalization, your objects opinion about what objects are important no longer matters. That is why it's dangerous to try to use any object member fields.
But I don't have a destructor, you say? Okay. In that case, when your object goes out of scope nothing will happen. Of course, if you actually have the responsibility to clean something up this is not so good...
Enter Dispose(bool disposing)
Microsoft has established a convention to make these two different cases easy to distinguish in code. In a nutshell:
public class A : IDisposable {
~A() {
Dispose(false);
}
protected virtual void Dispose(bool disposing) {
if(disposing) {
// Somebody actually called Dispose()!
// Let me send mail and open a web browser here
}
// Put code here that needs to run even if we are just called by the finalizer
// Remember, it should only clean up unmanaged resources. No referring to member objects!
}
public virtual void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
}
The "disposing" parameter is confusing. Sometimes, people write it as "finalizing" and reverse the true and false constants in the code above. Stick with disposing though, it seems to be winning the war of convention. Just remember: if disposing is true, then I came in here from an explicit call to Dispose(), and not from a finalizer.
I recommend to always create a protected virtual void Dispose(bool disposing) method in your class that implements dispose. Then, make your implementation of Dispose() look exactly like the code above. Make your destructor only do one thing...call Dispose(false).
More enhancements
This is purely syntactic sugar, but lots of folks like to do this:
public class A : IDisposable {
~A() {
Dispose(false);
}
protected virtual void DisposeManagedResources() {
// Send mail and open a web browser here!
}
protected virtual void DisposeUnmanagedResources() {
// Close file handles here. Be careful what you do!
}
protected virtual void Dispose(bool disposing) {
if(disposing) {
// Somebody actually called Dispose()!
DisposeManagedResources();
}
DisposeUnmanagedResources();
}
public virtual void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
}
If you follow this pattern you can forget about the Dispose() method alltogether. You can forget about remembering what to do if disposing is true or false. You just put your two kinds of cleanup code in the provided methods. We do this pretty often in our codebase.
Another enhancement is a disposed boolean variable. This ensures that cleanup is only done once. I think that if you set your fields to null, and do a null test on the way in then you don't need this, but perhaps I'm saving a penny to lose a pound. :-). Here is the code above modified to use a disposing variable. NOTE: this doesn't make the code any more thread safe. It just frees you from having to be careful in your cleanup code when it comes to null checks.
public class A : IDisposable {
private bool disposed;
~A() {
Dispose(false);
}
protected virtual void DisposeManagedResources() {
// Send mail and open a web browser here!
}
protected virtual void DisposeUnmanagedResources() {
// Close file handles here. Be careful what you do!
}
protected virtual void Dispose(bool disposing) {
if(!disposed) {
if(disposing) {
// Somebody actually called Dispose()!
DisposeManagedResources();
}
DisposeUnmanagedResources();
disposed = true;
}
}
public virtual void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
}
Hey, this is getting hard!
Stay with me man, breath! We are almost there. Now, for a simple class the code we have above is perfect, and absolutely correct. If you are doing a code review, and you can't map the dispose logic of the class one-for-one to the logic above, then there is a bug in that code.
But... what about class hierarchies? This is actually a big problem. Because if your understanding of IDisposable for the one class case is a bit fuzzy, it will really fall apart when you consider a class hierarchy. But if you really understand the pattern above, and the reasons behind it, then you'll make correct and logical decisions when faced with creating, altering or debugging the dispose logic in a class hierarchy.
The implementer of IDisposable has responsibilities
In a class hierarchy, the first class (call it B) who chooses to implement the IDisposable interface has a very concrete responsibility:
- Implement public sealed void Dispose()
- Be nice and implement protected virtual void Dispose(bool disposing)
- Implement a destructor that calls Dispose(false)
Now, if you have class C, which derives from B, then you only have to do one thing in C to carry on the tradition of correct dispose behavior:
class C : B {
protected override void Dispose(bool disposing) {
if(disposing) {
// Dispose of C managed resources
}
// Dispose of C unmanaged resources here
base.Dispose(disposing); // Give B a chance to do it's work.
}
}
What if C doesn't have any special work to do for dispose? Neither managed nor unmanaged resources to clean up? In that case C doesn't have to implement Dispose(bool disposing). Yay.
Note the call to the base class Dispose(disposing) method. That is very important. Note that this call is at the end of the Dispose(disposing) method. This reflects that C's resources are created after B's resources, and therefore C's resources should be cleaned up before B's resources are.
I don't have to implement IDisposable do I?
One case where you should implement IDisposable but is not very obvious is when your member variables implement IDisposable. The problem is what if your member variable is a FileStream. Now, it's true, when your object disappears, eventually the finalizer will run and find the FileStream object, call it's destructor, which will call Dispose(false) and the unmanaged resources will ultimately be cleaned up. But it's kind of rude.
You see, the FileStream advertised that he is ready and willing to clean up the moment you snap your fingers and call Dispose() on him! But if you don't implement IDisposable yourself so that a user of your class (who can't see the FileStream) can take advantage of that behavior, well then...a baby cries somewhere. Very loudly.
Of course, if you make sure to clean up that member variable before your object falls out of scope (maybe you have a "Close()" method or some convention like that), then you can sleep well again. However, it's pretty easy to implement IDisposable, now that you know what's going on!

