IDisposable and Using

In a comment on my last post, Alex Peake points out the Dispose() method and the using keyword. This is what I referred to in the post as “implementing IDisposable”, but I realize that I glossed over that too fast.

IDisposable is an interface that defines a single method, Dispose(). If you have “valuable resources that have to be put back on the shelf” the recommendation in .NET is that you define your class as implements IDisposable  and release the resources in the Dispose() method.

C# goes a step further and provides a keyword, using, that generates a try…finally block and, in the finally block, calls the Dispose() method of the IDisposable object that the using keyword refers to, for instance:

using(myNetworkConnection){
    doStuff();
 throw new EvenIfItThrowsAnException();
} //when execution reaches here, myNetworkConnection.Dispose() will be called

This is how you should get rid of valuable resources, not wait around for the finalizer to be triggered by a call from the garbage collector.

But that raises the question: if IDisposable and Dispose() are the .NET-recommended ways to dispose of non-memory resources, what is the purpose of the finalizer? That is, what are the recommended contents of Object.Finalize() other than a “last chance” call to: if(this is IDisposable) this.Dispose() ? And if those are the intended contents, why not just emit that IL instead?

Defending C# (and .NET’s) Garbage Collection

I read Bryan Flamig’s criticism of C# via Loren. Bryan voices a common criticism of C#’s garbage collection (the criticism actually would apply to all languages that don’t go beyond the default memory management model of CLR, but let’s just continue to use C# as the example): in C++, the timing of when an objects is “cleaned up” is under program control (it’s fully deterministic), while in C#, the default behavior is that the timing is non-deterministic (in fact, reclaiming memory is an aspect of “cleaning up” whose precise timing is very explicitly beyond the control of the developer).

Bryan makes the excellent point that “cleaning up an object” certainly means releasing the memory for reuse but can also mean closing a network socket, database connection, file handle, etc. There are many examples of valuable resources such as these that come from a limited pool, and it’s perfectly understandable for a programmer to want to control the timing of when such resources are “put back on the shelf,” as it were.

In C++, releasing resources and releasing memory are thought of as one event. In C# and most other .NET languages, the act of cleaning up is thought of as being two events: the release of valuable resources (finalization) and the release of memory (destruction). By default, the CLR’s garbage collector guarantees that it will call a “finalizer” function prior to releasing an object’s memory. So, if you use the finalizer method to control your valuable resources and do nothing else, “cleaning up” is linked to the non-deterministic garbage collector. That’s the chief criticism of C#’s garbage collection.  

But, to be fair, these non-memory resources are not what garbage collection aims to solve. Garbage collection aims to solve managing physical memory. Allocating and deallocating memory has unique problems (in that memory can point to other memory) and characteristics (the ratio of memory allocations and deallocations to, let’s say, the number of file handles opened and closed is approximately a zillion to one). So relying on an algorithm for garbage collection based on physical memory to time the clean-up of your valuable resources is a questionable strategy. But that doesn’t mean that garbage collection (of physical memory) is not a great advantage: the garbage collector is fast, it compacts the heap, and it goes a long way to help the most common types of programming bugs in C and C++. It doesn’t solve memory management, but it helps a lot.

So if you don’t want to rely on the garbage collector to trigger your finalizer to release your valuable resources, what do you do? You implement IDisposable and put the release of valuable resources entirely under programmatic control. Sure, that’s an error-prone burden, just like managing physical memory was. But it’s a start.

Now, can one imagine a CLR that had an algorithm that was optimized for managing non-memory resources? Yeah, but it might have poor performance. Or can one imagine some in-language facility that provides “deterministic finalization”? Yeah — C++/CLI will have such a thing and it’s certainly on the radar of the designers of the C# language.

But memory management via garbage collection? .NET provides a great implementation of a great idea.