Every now and then I still find myself scratching my head when adding a finalizer to a class or implementing IDisposable in a C# application, so I thought I would do a fast write up that sure could come handy the next time. By the way, I love JavaScript and firmly think one can write incredibly beautiful and complex code with it, but at the same time it keeps you so shut down from Resource Management or Concurrency that basic knowledge on these areas all of a sudden looks like "hardcore programming".
There are tons of resources around about this, but this is one of the most complete ones. From that and many other articles, we should know that the common pattern for a class implementing IDisposable and containing a Finalizer is something like this:
public class DisposableClass : IDisposable { ~DisposableClass() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { // Clean up all managed resources } // Clean up all native resources } }
So far so good. I'm not going to explain how the above works, or how the GC and Finalization work, it's explained in detail in thousands of places (indeed I also wrote about .Net's GC before [1] and [2], I'll just recap below a few points that seem useful to me:
- So, when should I care about implementing such pattern in my own classes?
Basically, if your class directly owns an unmanaged resource (a Windows handle mainly) you'll need to write a finalizer that takes care of releasing such unmanaged resource. - OK, and then, does having a Finalizer mean that my class has to implement IDisposable?
In short, Yes. Well, when you write a Finalizer you make sure that the unmanaged resources will be eventually released by the GC-Finalizer maphia. As GCs take place in a non deterministic way you could play better with the whole system by allowing consumers (owners) of your objects to release these resources when they are done with your objects. For this, you should implement IDisposable, so that these other guys can directly call to you Dispose() method (or indirectly by means of the using clause). Notice that as reflected on the code above, such Dispose should invoke GC.SuppressFinalize, this way your object is removed from the finalization queue, and the GC-Finalization will work faster.
This said, someone noted in StackOverflow that there are a few classes in the Framework (e.g. Threading.Thread and WeakReference) which have finalizers but do not implement IDisposable. - Understood, but one more thing, does it make sense to implement IDisposable but not having a finalizer?
Yes it does. If your class does not hold unmanaged resources, it won't have a finalizer, but it could hold instances of other IDisposable classes. In that case, you must implement IDisposable and invoke the Dispose() on those instances from your own Dispose(). Furthermore, your objects could want to run some other final actions when they are no longer needed (write to a log...) so that should also go in your Dispose, and well behaved consumers would remember to invoke it.
An important point to reckon is being careful in our finalizers not to invoke actions in other objects that could no longer be valid (cause their finalizers have run earlier than ours). I mentioned it here saying that you should refrain from calling a finalizable object from your finalizer. Well, indeed it's not just calling into finalizable objects, it's calling to any object implementing IDisposable what you have to avoid.
Let's say we have:
- class A has a finalizer, is IDisposable and holds a reference to class B.
- class B has no finalizer, but is IDisposable cause it has a reference to class C
- class C has a finalizer and is IDisposable
Before ending this post I'd like to mention how the special syntax (~MyFinalizer) defined by the C# team to facilitate the writing of Finalizers can add some confusion to people with some C++ background.
Annotation (Joe Duffy): Earlier in the .NET Framework’s lifetime, finalizers were consistently referred to as destructors by C# programmers. As we become smarter over time, we are trying to come to terms with the fact that the Dispose method is really more equivalent to a C++ destructor (deterministic), while the finalizer is something entirely separate (nondeterministic). The fact that C# borrowed the C++ destructor syntax (i.e. ~T()) surely had at least a little to do with the development of this misnomer. Confusing the two has been unhealthy in general for the platform, and as we move forward the clear distinction between resource and object lifetime needs to take firm root in each and every managed software engineer’s head.
No comments:
Post a Comment