If you turn on Code Analysis for the first time on a big project, it’s almost inevitable that you’re going to get hit with a CA1063: Implement Dispose correctly. In fact, chances are you’re going to get hit with it on every single one of your IDisposable classes because the correct way to implement Dispose is something that you won’t think about it until you get warnings.
The CA link does a pretty good job of showing you how to implement it correctly. Let’s talk a little bit about why “correct” is correct.
First, you need to create an overload of Dispose that takes a bool and make it protected and virtual, like this:
1 | protected virtual Dispose(bool disposing) { ... } |
You might be thinking to yourself, “If they wanted Dispose to take a parameter why didn’t they just make the IDisposable interface declare this method?” Well, that’s simple.
First, consider what Dispose is supposed to do. It’s supposed to release resources. Often times, developers have it in their mind that IDisposable isn’t something they need to worry about because “resources” means unmanaged pointer-ish native interop crap that a pure managed class has nothing to do with. But “resources” can also mean things like file handles and network connections. There’s little more frustrating than an application that doesn’t close its file handles correctly. Have you ever gotten that annoying error from Windows that a file is in use by a process so it can’t be moved/renamed/deleted/modified? Don’t add to that problem.
The truth is that many C# base classes, like FileStream, hold resources that should be freed and thus implement IDisposable. Code Analysis will catch you if you try to create a class which declares a field (or auto-implemented property!) which is IDisposable, but your class does not implement IDisposable. If your class has a FileStream member, you must make your class implement IDisposable, and in the Dispose method, you must dispose your FileStream field.
The problem with IDisposable is that the Dispose method which it defines is optional. The compiler won’t even warn you if you don’t call Dispose on an IDisposable before it goes out of scope (but Code Analysis will). Its purpose is to augment class finalizers (you know, the funky ~ClassName() method), not replace it. The finalizer is invoked by the garbage collector when it is destructing (or if you prefer, dis-instantiating) your class. The garbage collector will not invoke Dispose if the instance happens to implement IDisposable.
Because there are two cases where your class could be effectively “destroyed” – when a user calls Dispose explicitly (or implicitly in a using keyword construction), or when the garbage collector invokes the finalizer - this means you’d need to put your cleanup code in two places – the body of Dispose() and the body of a class finalizer.
Thus, the first step in implementing IDisposable pattern correctly is to define a single method which we intend both the finalizer and the Dispose body to call. The parameter, disposing, indicates from which method our protected virtual Dispose is being called – if true, it’s being invoked from Dispose and if false it’s being called from the finalizer.
Because of this, we don’t want to make our overload public. All public callers will call Dispose() with no parameters and we will then invoke Dispose(true), indicating that we’re cleaning up at the explicit request of a caller. Since this overload shouldn’t be public, it can’t be part of an interface, because interface methods are inherently public. That’s why this overload is not part of IDisposable.
It’s virtual because a subclass may add new fields or properties that are IDisposable implementers and the subclass will then need to hook into our Dispose method.
With me so far?
The next question you may have is why do we care whether Dispose is being called from the public Dispose method (explicitly) or by the finalizer (implicitly?).
Well, because there are two different types of resources that could need to be released: managed resources and unmanaged resources. This may surprise you.
A managed resource is something like the FileStream class – you’re closing a file handle, but your FileStream field is still a CLR object.
An unmanaged resource is something like an IntPtr to a block of memory allocated on the unmanaged heap.
The difference – and why we care – has to do with subtle and often misunderstood details of the way finalizers work.
When a class finalizer executes, you are not allowed to make any assumptions about the validity of any reference instance. A reference type is any class. (A value type is any struct). What this means is that if you were to try to do something with that FileStream field in your class destructor, like call its Close method to ensure the file handle is released – you could get a variety of nondeterministic behavior. The field could already be null, or it could be in the middle of being finalized itself which puts it in an unpredictable state.
Does that mean your file handle is locked forever?
Well, actually, no, because the FileStream’s finalizer handles that for you, and while you can’t really do anything with your FileStream field in your class’s destructor, you don’t need to worry about it. The reason you can’t do anything with it is because the CLR finalizes it for you, and FileStream handles its own handle.
But what about our friend the IntPtr, which is pointing to some memory that we allocated manually? That’s another story. The IntPtr is a struct, which means there’s nothing to finalize. The memory occupied by that struct is part of your class instance’s heap size. It’s only freed after your finalizer is done executing.
That’s what you do inside a finalizer – you free unmanaged resources pointed at by value type members of your class.
So, consequently, that’s what you do in your Dispose() method when the boolean disposing parameter is false, and your finalizer becomes:
1 | ~YourClass() { Dispose(false); } |
So what about your Dispose() method, then? Perhaps:
1 | public void Dispose() { Dispose(true); } |
Almost, except there’s one extra step, which should come after calling Dispose(true), and that’s a call to the garbage collector class, GC, instructing it not to call the finalizer when it deallocates the memory used by the instance you’ve just disposed. The reason is that when Dispose is called explicitly, you should clean up all the resources – managed and unmanaged – not just some of them.
The general pattern of the Dispose(bool disposing) method, then looks like this:
01 | protected virtual void Dispose(bool disposing) |
02 | { |
03 | try |
04 | { |
05 | if (disposing) |
06 | { |
07 | // call Dispose on all your disposable fields/properties |
08 | } |
09 | } |
10 | finally |
11 | { |
12 | // free all unmanaged resources here. Note that this is not in an else block. |
13 | } |
14 | } |
It’s important to make sure your unmanaged resources are released even if something goes wrong disposing your managed members.
Thus, Dispose becomes this:
1 | public void Dispose() |
2 | { |
3 | Dispose(true); |
4 | GC.SuppressFinalize(this); |
5 | } |
Once these three methods are implemented to this pattern – Dispose(), Dispose(bool disposing), and your finalizer – Code Analysis is satisfied.
However, it shouldn’t be, because you’re not even halfway done.
What’s missing, you ask?
Well, remember, Dispose() is different than the finalizer. The finalizer only gets called when the memory model concludes that no running code could ever possibly reference the instance it’s finalizing, so there’s no chance anyone will call a method on it again. But Dispose can be called explicitly at any time, making code like this perfectly legal:
1 | var fs = new FileStream(...); |
2 | fs.Close(); // calls Dispose() |
3 | fs.Read(...); |
The call to Read() after Close() throws an instance of ObjectDisposedException. How nice of the BCL implementers to do that.
Guess what, oh ye brave implementer of IDisposable? You need to do that too.
Generally, once Dispose() is called, all instance methods, and possibly its properties, must throw ObjectDisposedExceptions. This is for two reasons. First, it indicates immediately to your caller that they have made an error by continuing to use an instance that has previously been disposed, and second it protects you from unexpected unhandled exceptions at strange places. Continuing to use deallocated memory - whether that’s a pointer in C++ or a disposed instance in .NET – has been by my estimation the cause for at least 25% of application crashes in all of the history of computer software.
The easiest way to do this is to define a boolean field or property, IsDisposed, which you set in your protected Dispose method. Then, create a helper method like this:
01 | /// <summary> |
02 | /// Throws an <see cref="ObjectDisposedException"/> if <see cref="IsDisposed"/> is <see langword="true"/>. |
03 | /// </summary> |
04 | /// <exception cref="ObjectDisposedException">if <see cref="IsDisposed"/> is <see langword="true"/></exception> |
05 | protected void ThrowIfDisposed() |
06 | { |
07 | if (IsDisposed) |
08 | { |
09 | throw new ObjectDisposedException("This instance has been disposed."); |
10 | } |
11 | } |
At the beginning of each method and property which relies on the members which have been released by Dispose(), add a call to ThrowIfDisposed().
Update: as commentor Mohan pointed out, the documentation on the Dispose method clearly states that the Dispose() method must ignore multiple calls to Dispose. This is interesting since there is a code analysis warning, CA2202, which checks for multipe calls to Dispose, which banks on the idea that Dispose is not always implemented correctly. You can do as Mohan suggests by checking IsDisposed in the Dispose method and returning, or you can take care to not attempt to multiply release resources. The easy way to do that is to change the state of disposable members, e.g., check that a member is not null. If it is not null, dispose it and set it to null. This way, calling Dispose multiple times will not have side effects. You must be careful, however, as none of these approachs is thread-safe unless you make it thread-safe, which is usually a good idea.
Because IDisposable is a common interface to implement and doing so requires following a very specific pattern that is identical in every case (sans the implementation of the Dispose(bool) overload), I created a code snippet that implements Dispose correctly. With a few keystrokes I get all of the “boilerplate” code I need to not only avoid the Code Analysis warning, but do it right and do it more thoroughly than Code Analysis requires.
Download the Disposable code snippet here!
The attachment is not a document. It’s a Visual Studio xml snippet. Trim the .doc extension from the filename. WordPress doesn’t allow me to upload files with arbitrary extensions.
Enjoy!
Tags: Dispose, what is dispose()