Ben Tsai

IDisposable: The contentious Dispose pattern

Yucky stuff #

I convinced myself that I needed to take some action with the IDisposable interface, so I started to do some research on best practices. The first place I looked was the MSDN documentation, where I found the "official" pattern:

[sourcecode language="csharp"] public class Base: IDisposable { private bool disposed = false;

//Implement IDisposable. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }

protected virtual void Dispose(bool disposing) { if (!disposed) { if (disposing) { // Free other state (managed objects). } // Free your own state (unmanaged objects). // Set large fields to null. disposed = true; } }

// Use C# destructor syntax for finalization code. ~Base() { // Simply call Dispose(false). Dispose (false); } } [/sourcecode]

The first thing I noticed was that it doesn't look very elegant. This surprised me because, in general, the .NET framework is very elegant and well-crafted. What's this business with calling GC.SuppressFinalize, the disposing flag, and the scary C# finalizer with C++ destructor syntax? (I will hereon refer to them as "yucky stuff.") There must be a better way, or there's something I'm not understanding here. And I didn't want to go blindly apply this pattern everywhere before understanding it some more. This prompted more digging.

What Your Mother Never Told You About Resource Deallocation #

Stephen Cleary wrote an excellent article that covers the problems with IDisposable and also proposes some of his own best practices that simplify the pattern significantly. I highly recommend reading the entire article. He validates my reaction that IDisposable objects are cumbersome to use, and that the Microsoft-endorsed pattern is needlessly complex.

The main issue is that the pattern is designed for the general case that handles both explicit and implicit resource cleanup. But I believe most .NET developers are normally concerned only with explicit cleanup, and so most of the yucky stuff becomes extraneous.

As I learned, if you design your classes properly, you can simplify the pattern significantly:

[sourcecode language="csharp"] public sealed class BetterWay : IDisposable { Font coolFont; public BetterWay() { coolFont = new Font("Comic Sans", 12); }

public void Dispose() { coolFont.Dispose(); } } [/sourcecode]

What happened to all the yucky stuffs? Stephen Cleary's "Disposable Design Principle" makes them go away:

Level 0 types only deal with unmanaged resources. Level 1 types only deal with managed resources, and thus, implementing IDisposable for Level 1 types is simple. Since most .NET developers live in Level 1-land, they can use this pattern and be happier. Regarding this implementation, Stephen Cleary notes:

He also covers this implementation, which he calls "The Second Rule Of Implementing IDisposable" on his blog. (Implementing IDisposable for Level 0 types is harder; you should read the rest of the article and refer to "The Third Rule of Implementing IDisposable.")

Feeling good about it #

Stephen Cleary's best practices were looking more attractive, but I needed more evidence to feel comfortable about deviating from the Microsoft-sanctioned way. As I searched more, I found others who supported my desire to use a different pattern. This Stack Overflow answer and follow-up comments debate this very topic. Jon Skeet, as usual, does a good job of explaining the issue, and states that "if you seal your classes, it makes life a lot easier: the pattern of overriding Dispose to call a new virtual Dispose(bool) method etc is only relevant when your class is designed for inheritance." I found another post that was similar in spirit. And even a proposal for changing C#, prompted by the difficulty of the official pattern.

Now that I understand things a lot better, I'll be using this new pattern when designing my classes. Less yucky stuff.

Monday, February 21, 2011