I was thinking about this just today whilst I was writing some IDisposable
code.
It's good practice for the developer to either call Dispose()
directly, or if the lifetime of the object allows, to use the using
construct.
The only instances we need to worry about, are those where we can't use using
due to the mechanics of our code. But we should, at some point, be calling Dispose()
on these objects.
Given that the C# compiler knows an object implements IDisposable
, it could theoretically also know that Dispose()
was never called on it (it's a pretty clever compiler as it is!). It may not know the semantics of when the programmer should do it, but it could serve as a good reminder that it never is being called because it was never used in a using
construct, and the method Dispose()
was never called directly, on any object that implements IDisposable
.
Any reason for this, or are there thoughts to go down that route?
it could theoretically also know that
Dispose()
was never called on it
It could determine in certain simple cases that Dispose
will never be called on it. It is not possible to determine, solely based on a static analysis of the code, that all created instances will be disposed of. Code also does not need to be very complex at all to get to the point to which even estimating if objects are left undisposed is straightforward to do.
To make matters worse, not all IDisposable
object instances should be disposed. There can be a number of reasons for this. Sometimes an object implements IDisposable
even though only a portion of their instances actually do anything in the implementation. (IEnumerator<T>
is a good example of this. A large number of implementations do nothing when disposed, but some do. If you know what the specific implementation you have won't ever do anything on disposal you can not bother; if you don't know that you need to ensure you call Dispose
.
Then there are types such as Task
that almost never actually need to be disposed. (See Do I need to dispose of Tasks?.) In the vast majority of cases you don't need to dispose of them, and needlessly cluttering your code with using
blocks or dispose calls that do nothing hampers readability.
The major rule regarding IDisposable
is "would the last one to leave the room, please turn off the lights". One major failing in the design of most .NET languages is that there is no general syntactic (or even attribute-tagging) convention to indicate whether the code that holds a particular variable or class that holds a particular field will:
If languages had a syntax to distinguish among those cases, then it would be simple for a compiler to ensure that things which know they're going to be the last one to leave the room turn out the lights and things which are never going to be the last one to leave the room don't turn out the lights. A compiler or framework could facilitate the third and fourth scenarios if the framework included wrapper types that the compiler knew about. Conventional reference-counting is generally not a good as a primary mechanism to determine when objects are no longer needed, since it requires processor interlocks every time a reference is copied or destroyed even if the holder of the copy knows it won't be "the last one to leave the room", but a variation on reference-counting is often the cheapest and most practical way to handle scenario #4 [copying a reference should only increment the counter if the holders of both the original and copy are going to think that they might be the last owner, and destroying a copy of a reference should only decrement the counter if the reference had been incremented when that copy was created].
In the absence of a convention to indicate whether a particular reference should be considered "the last one in the room", there's no good way for a compiler to know whether the holder of that reference should "turn out the lights" (i.e. call Dispose
). Both VB.NET and C# have a special using
syntax for one particular situation where the holder of a variable knows it will be the last one to leave the room, but beyond that the compilers can't really demand that things be cleaned up if they don't understand them. C++/CLI does have a more general-purpose syntax, but unfortunately it has many restrictions on its use.
The code analysis rules will detect this. Depending on your version of VS you can either use FXCop or the built in analysis rules.
It requires static analysis of the code after it has been compiled.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With