Disposable value types

Way back when, Ian Griffiths wrote a disposable class called TimedLock that made it easy to use a C# using statement to release the lock. Eric Gunnerson advised him to use a struct instead of a class because a value type avoids a heap allocation. Some time later, Phil Haack verified that the using statement doesn’t box the value type in order to call Dispose (as long as IDisposable is implemented explicitly, not implicitly).

Disposable value types do make for a nice optimization in C#, but like many optimizations, they should only be used when performance demands it, because they have a few disadvantages.

Most importantly, there’s nothing to keep the Dispose logic from being called twice, perhaps even accidentally. Chris Lyon touches on this when he explains why GCHandle doesn’t implement IDisposable. If you make a copy of a disposable value type before you call Dispose, there is no way to avoid executing the same logic if Dispose is called on each copy. This could cause an unmanaged resource to be freed twice, or cleanup code to be executed twice, etc. A class doesn’t have this problem because it is only copied by reference; once Dispose is called, it can make sure that subsequent calls to Dispose do nothing.

Another disadvantage of disposable value types is that C++/CLI doesn’t support them very well. In C++/CLI, you can’t define disposable value types at all; compiler errors prohibit you from defining a destructor or a Dispose method on a value type. You can use a disposable value type defined in C#, but you can’t dispose one without boxing it, which causes a heap allocation, thus subverting the purpose of the optimization.

Also, consider this: If the creation or disposal logic of the value type happens to require a heap allocation (creating a delegate, for example), it is unlikely that the optimization will come to much, since the whole point of a disposable struct is to avoid heap allocation.

In summary, I’m not using disposable value types with the reckless abandon that I once did. But if performance measurements ever show that I’m spending too much time allocating a disposable class that’s used in this way, I know that I can try making it a struct.

Posted by Ed Ball on February 27, 2008