When .NET was first created, there was uncertainty about how IDisposable
should be used. As a result, IDisposable
was applied in an overly aggressive fashion with many categories of classes requiring empty Dispose methods. This has led to problems with static analysis tools that cannot separate real cases of missing Dispose
calls from false positives.
To understand why, you have to go back to the early history of the CLR and how garbage collection works. Originally the CLR was meant to be the new runtime for Visual Basic, which in the late 1990s was based on COM. Under the COM model, objects have a reference count. As references are created and destroyed, the reference count is updated accordingly and if the count ever goes to zero, the object is deallocated. This creates a deterministic garbage collection model, where you can tell exactly when resources are going to be cleaned up.
The most significant downside of a reference collecting garbage collection model is it can easily leak memory. This happens when you have a series of objects referencing each other in a loop, each object preventing the reference count of the others from dropping to zero. It also can lead to performance problems in multi-threaded environments because locks are needed when adjusting the reference count.
Early in its development, Microsoft decided the CLR would avoid these problems by choosing a mark-and-sweep garbage collector. This was already well established in the broader community through Java’s popularity. But this style of GC does not deterministically free resources, making it inappropriate for database connections, file handles, and other highly limited resources. Hence the creation of IDisposable.
At the same time, Microsoft was experimenting with the concept of “components”. The idea of a component was never really well defined. There is a Component
class, along with interfaces such as IComponent
, IContainer
, and ISite
. After nearly two decades, the documentation still only has a vague comment about “object sharing between applications”. Presumably the thought was it would be like COM, where one application can directly interact with objects in another program. But that didn’t really work out so it’s mostly forgotten history.
Meanwhile in Windows Forms there was a different notion of “component” that really meant “something that can be placed on a form/window”. Aside from actual UI elements such as text boxes, this would include objects that added a capability such as timers. This comes from the VB 6 era of programming where almost anything you want to use has to be dropped onto the form itself. Even database connection and commands could be placed directly onto the form.
And this is how we ended up with seemingly nonsensical objects being marked as IDisposable
. Classes like DataTable
and SqlCommand
have no unmanaged resources to dispose. But because in the past we mistakenly thought it would be good to put them on a form, they inherited from the Component
class. And again, Component is disposable so that you can choose when to close proxy objects.
Static Analysis
As static analysis slowly continues to move from an advanced tool to something everybody is expected to use, warnings about disposable objects increasingly become a problem. It’s not too bad for short-lived objects such as SqlCommand
, as it is easy enough to wrap a using statement around it without thinking about the fact the statement doesn’t actually do anything.
It’s harder for stuff like DataTable
. This is an object meant to be long-lived and can end up being used far from the place where it is created. Unless suppressed or disabled, the static analysis tools will report warnings and errors about DataTable and similar objects not being disposed.
DisposeUnusedAttribute Proposal
The ‘best’ solution would be to simply remove all of the unused Dispose
methods. But that’s not really an option because it would break backwards compatibility.
Edward Brey has proposed a rather simple but elegant solution. He suggests creating an DisposeUnused
attribute that would silence static analysis tools. Subclasses would not inherit this attribute.
This design is not without flaws. Once DisposeUnused
is applied to a class, it would be a breaking change to remove it. For DataTable that’s not a concern, but Stephen A. Imhoff offers a counter example where it could be:
It's actually worse, because now you have something that says "yeah, ignore this contract that I claim to need", and if a type suddenly did have to dispose a resource (say, MemoryStream starts allocating a native array for really large array sizes or something), your consumers now need to dispose, but you previously informed them they didn't ...
Another issue being you don’t always know what’s in a variable. Say you have a variable of type Component
. At compile time, there is no way to tell if what you put into the variable will need to be disposed. Thus, it may make more sense to only apply DisposeUnused
to sealed classed where further subclassing isn’t possible.