IDisposable for dummies #2 – A guide about ‘how to implement it’


IDisposable - How ?In my previous post, I classified the different memory resources available in the .NET CLR and I explained the role of the IDisposable interface as well as the use of the Finalize() method.

In this post, I am going to talk more about the implementation details of IDisposable and Finalize().

For reference, I have split this matter into two posts:

You should know by now when and why you should implement IDisposable and Finalize() method according to the type of resource(s) your class owns. In order to answer the question “How?” we still do need to look at two more aspects: 1) Inheritance (as in Object-Oriented Programming) & 2) Some remaining considerations.

OOP – Inheritance

There is one more aspect to take into consideration in object-oriented programming and that is inheritance. When you define a public base class (or any public class that can be instantiated and is not marked as “sealed”) you should consider that even though you have not any managed or unmanaged resources, you might still need to think about the potential classes that would derived from it. They might require managed or unmanaged resources. In that case, you should make the finalization code (IDisposable + Finalize()) in your base class, so that it is part of the contract and derived-class or client-code could use it correctly (i.e. calling Dispose() whenever it is required).

Obviously, if you are SURE to be  in control of ALL the client code of your class, then you can wait for your derived classes to really require this resource management code (IDisposable + Finalize()) before implementing it.

As for me, as soon as I create a new class, my first step is to make it “sealed” – I found this “habit” very effective, because it closes a lot of questions about inheritance (which method must be declared “virtual”, which method/properties/field should be declared “protected”, …). If later the class must become a base class, then I can review the changes and needs at that time. This is my way, everyone is different 🙂

 Some remaining considerations

Finalizers:

  • Be careful, if an exception is thrown in the constructor, the finalizer code will still be executed, so make sure to check whether a field (to a native resource) has been correctly initialized before calling any clean up code on it.

Dispose() method:

  • To help to ensure that resources are always cleaned up appropriately, a Dispose() method should be callable multiple times without throwing an exception.
  • The object must dispose all of the resources it owns.
  • A disposed object cannot be used anymore (i.e. once its Dispose() method has been called), so it must become “garbage” so next time the garbage collector will kick off its managed memory can be reclaimed (or if it has a Finalizer(), it will be place on the “f-reachable” list).
  • Once an object has been disposed, all of its public methods must throw the exception of type “ObjectDisposedException”.
  • You must make sure that no delegates (from Rooted object) are pointing to it, usually done via “events“. Here, you have two choices:
    1. The instance which is running Dispose() exposes event(s) (aka source of event(s)), then it must reset the list of its subscribers (you might want to define a ‘OnDisposing’ event, so that client code might register and have a chance to be notified when Dispose() is called on the source).
    2.  The instance which is running Dispose() consumes event(s) (aka subscriber of event(s)), then it must unsubscribe itself from the source(s) of events.

Versioning:

  • You have to be careful when adding “IDisposable” to your class.
    • If you are in control of all the assemblies using it, then you can add it and review its usage amongst all your code.
    • If you have shipped or distributed your class to customers/partners, you cannot add it like that, because this change the semantics of your class and the client code is not aware of it. In that case, one option to make your existing class “Obsolete” and create a new one with the implementation of IDisposable and let your clients know about the new class.

Native Handle

  • Since .NET 2.0, Microsoft has released a new abstract class ‘SafeHandle‘ [Ref-01 & 02] that can help you in disposing native handles. Most of the .NET classes that wrap native handles (File, Pipe, Wait handle, Registry key, …) use internally specialized classes derived from ‘SafeHandle’ (read the doc on .NET v4.0 or above of MSDN about this subject). So you might consider, if needed, to create your own SafeHandle derived class for disposing native handles.

Decision Matrix

So, now we have all the bits of information that should be taken into consideration to find out what really is needed in the code for your class:

  • Sealed class or base class?
  • What type of resources does your class own?

Once you answered to those questions, then the matrix below provides you with the bits that are required for you case:

Nota:

  • Finalize():
    • Must only release unmanaged resources. E.g. native/unsafe handles on file, windows etc.
      No cleaning code on .NET object field(s) should be done inside a Finalizer. (i.e. calling Dispose(), Close() or removing event handlers …).
  • Dispose(bool): implement it when the release logic is shared by Dispose() and Finalizer()

Here is a link for downloading a sample solution (VS 2010, C# source code).

The sample code I am providing respect the decision matrix above (and is deliver “AS -IS”, the usual formula for “disclaim”). However, because of refactoring and reuse of code you will find some differences. E.g. for “non-sealed class” instead of defining a “protected virtual Dispose(bool)” method I instead defined a “private” one with the hard-coded “Dispose(bool) logic” in it and defined two virtual methods: one for disposing Managed Resources and one for Unmanaged Resources (I did not update the decision matrix, because it is close to what you can find on current MSDN documentation, so most developers should be familiar with it – the principles are here).

Fx-Cop to the rescue

If you are using Visual Studio 2005 and above and have the “FX-Cop” as part of your edition (also referred as “Code Analysis”) it is a good practice to enable the following checking rules:

This way, every time you add code to your project, FX-Cop can look for you for new usage of Disposable resources and warn you about your usage in your code. This is not bullet proof (I had some cases, long chain of inheritance, where FX-Cop did not detect and did not warn about the not use of IDisposable.Dispose() – but this was more the exception than the rule, so use it).

Conclusion

That is it for me. I hope you have now a better understanding about “how to implement IDisposable”.

References:

Leave a comment

8 Comments

  1. Tibi

     /  May 1, 2012

    According to your chart you reccomend adding a Finalize method in all base classes. I wouldn’t add a Finalize method in any base class, unless the base class itself uses any unmanaged resources. If you have a Finalizer in the base class, all the instances of the derived classes will be added to the F-reachable queue.

    Reply
    • Hello Tibi,

      This is one of the main points of this article and one of the pain points I have seen in various systems. Thank you for your comment.

      “If you have a Finalizer in the base class, all the instances of the derived classes will be added to the F-reachable queue.”
      You are right and I agree with this statement.

      And “yes” I DO recommend an overridden “Finalize()” method in the base class which implements “IDisposable”.
      Why?
      There are 3 cases for “IDisposable” (as in the matrix) :
      1) “Managed resources only”.
      2) “Unmanaged resources only”.
      3) Both types of resources.

      As a base class (from an OOP), you are defining a “general contract” for your derived classes which will have a more specific implementation. As such, you should implement the most general contract for “IDisposable”, so it is the case #3 with “IDisposable + Finalize()”.

      Also, I do recommend it because having “IDisposable” logic with “Finalize()” at the same level is a lot easier for code maintenance.

      This is my recommendation, you can still choose to have your base class implementing only “IDisposable” and one of your derived class will override “Finalize()”. However, in this scenario you must still be able to suppress your derived class instance from the F-reacheable queue when “Dispose()” is called, especially for performance reasons. How do you do it?
      -> Your base class does not do it, because it has not overriden the Finalize() method, so its public “Dispose()” will not do the “GC.SuppressFinalize(this);”.
      -> Are you going to override the public method “Dispose()”? Or the protected method “Dispose(bool)”?
      -> Did the base class defined a virtual method for the public “Dispose()”? Or for the protected method “Dispose(bool)”? Or both?
      -> What if your derived class is not the “level 1” in your hierarchy of class? (i.e. it does not derived directly from the base class, so there could be one or more derived classes between yours and the base class).

      For me this lead to more questions and various solutions to implement than an explicit answer. So, in the end it increases the complexity of maintenance. This is what I found when I did the code review about a year ago: many inconsistent levels of “Dispose()” and “Finalize()” in various hierarchy of classes. Some methods were not “correctly” defined virtual, which lead to “release of resource” issues in Finalizers defined by derived classes (i.e. Dispose methods defined by derived classes in the middle of the hierarchy were not called properly). You can call that “bad development”, I just saw that happened many times after patches to code on PRODUCTION SYSTEMS where developers had (under pressure) to fix issues and were not allowed to change all the necessary code (we might see this as a bad process, but partial deployment is a reality for some of us).

      All those cases lead me to write both articles about “IDisposable”. It is a complex subject and I like to keep things simple and consistent whenever possible, especially for maintenance purposes.

      Again, this is my recommendation and the way I learned to do it for “IDisposable”. You are free to take the pieces you like about it and do otherwise in some cases.
      If you could share the way you do it for base/derived classes with the various type of resources, that will be great.

      I am still learning every day and I still do have an open mind 😉

      Thank you Tibi to bring this forward.

      Joao

      Reply
  2. Donald

     /  May 2, 2012

    Great article!

    Link to the specialized SafeHandles:
    http://msdn.microsoft.com/en-us/library/microsoft.win32.safehandles.aspx

    Reply
  3. Deleep Nair

     /  May 30, 2014

    Great article and thank you for showing me the way for best garbage disposal method 🙂

    My search for finding the best practice for implementing IDisposable interface came to end when I came across this article. I started to apply your rules in all my new classes and realized I was having to go back and reference your matrix whenever I have to implement IDisposable.

    To help me, I created code snippets which have been very helpful in the coding and thought it could help others too. So I created a project on codeplex recently and made available for everyone’s use (https://codesnippets.codeplex.com/).

    Hope you will like it.

    Reply
  4. dnair926

     /  May 30, 2014

    Great article and thank you for showing me the way for best garbage disposal method 🙂

    My search for finding the best practice for implementing IDisposable interface came to end when I came across this article. I started to apply your rules in all my new classes and realized I was having to go back and reference your matrix whenever I have to implement IDisposable.

    To help me, I created code snippets which have been very helpful in the coding and thought it could help others too. So I created a project on codeplex recently and made available for everyone’s use (https://codesnippets.codeplex.com/).

    Hope you will like it.

    Reply
  1. IDisposable for dummies #1 – Why? What? « Joao @ iLab8
  2. IDisposable for Dummies #1 – Why? What? | the pluralsight blog

Leave a comment