I was having a discussion with a colleague recently about the value of Dispose
and types that implement IDisposable
.
I think there is value in implementing IDisposable
for types that should clean up as soon as possible, even if there are no unmanaged resources to clean up.
My colleague thinks differently; implementing IDisposable
if you don't have any unmanaged resources isn't necessary as your type will eventually be garbage collected.
My argument was that if you had an ADO.NET connection that you wanted to close as soon as possible, then implementing IDisposable
and using new MyThingWithAConnection()
would make sense. My colleage replied that, under the covers, an ADO.NET connection is an unmanaged resource. My reply to his reply was that everything ultimately is an unmanaged resource.
I am aware of the recommended disposable pattern where you free managed and unmanaged resources if Dispose
is called but only free unmanaged resources if called via the finalizer/destructor (and blogged a while ago about how to alert consumers of improper use of your IDisposable types)
So, my question is, if you've got a type that doesn't contain unmanaged resources, is it worth implementing IDisposable
?
The Dispose method immediately releases the unmanaged resources. In the event that a consumer of your type forgets to call Dispose, provide a way for your unmanaged resources to be released. There are two ways to do this: Use a safe handle to wrap your unmanaged resource.
The following are two mechanisms to automate the freeing of unmanaged resources: Declaring a destructor (or Finalizer) as a member of your class. Implementing the System. IDisposable interface in your class.
The Dispose Method—Explicit Resource Cleanup Unlike Finalize, developers should call Dispose explicitly to free unmanaged resources. In fact, you should call the Dispose method explicitly on any object that implements it to free any unmanaged resources for which the object may be holding references.
I know from reading Microsoft documentation that the "primary" use of the IDisposable interface is to clean up unmanaged resources.
There are different valid uses for IDisposable
. A simple example is holding an open file, which you need to be closed at certain moment, as soon as you don't need it any more. Of course, you could provide a method Close
, but having it in Dispose
and using pattern like using (var f = new MyFile(path)) { /*process it*/ }
would be more exception-safe.
A more popular example would be holding some other IDisposable
resources, which usually means that you need to provide your own Dispose
in order to dispose them as well.
In general, as soon as you want to have deterministic destruction of anything, you need to implement IDisposable
.
The difference between my opinion and yours is that I implement IDisposable
as soon as some resource needs deterministic destruction/freeing, not necessary as soon as possible. Relying on garbage collection is not an option in this case (contrary to your colleague's claim), because it happens at unpredictable moment of time, and actually may not happen at all!
The fact that any resource is unmanaged under the cover really doesn't mean anything: the developer should think in terms of "when and how is it right to dispose of this object" rather than "how does it work under the cover". The underlying implementation may change with the time anyway.
In fact, one of the main differences between C# and C++ is the absence of default deterministic destruction. The IDisposable
comes to close the gap: you can order the deterministic destruction (although you cannot ensure the clients are calling it; the same way in C++ you cannot be sure that the clients call delete
on the object).
Small addition: what is actually the difference between the deterministic freeing the resources and freeing them as soon as possible? Actually, those are different (though not completely orthogonal) notions.
If the resources are to be freed deterministically, this means that the client code should have a possibility to say "Now, I want this resource freed". This may be actually not the earliest possible moment when the resource may be freed: the object holding the resource might have got everything it needs from the resource, so potentially it could free the resource already. On the other hand, the object might choose to keep the (usually unmanaged) resource even after the object's Dispose
ran through, cleaning it up only in finalizer (if holding the resource for too long time doesn't make any problem).
So, for freeing the resource as soon as possible, strictly speaking, Dispose
is not necessary: the object may free the resource as soon as it realizes itself that the resource is not needed any more. Dispose
however serves as a useful hint that the object itself is not needed any more, so perhaps the resources may be freed at that point if appropriate.
One more necessary addition: it's not only unmanaged resources that need deterministic deallocation! This seems to be one of key points of the difference in opinions among the answers to this question. One can have purely imaginative construct, which may need to be freed deterministically.
Examples are: a right to access some shared structure (think RW-lock), a huge memory chunk (imagine that you are managing some of the program's memory manually), a license for using some other program (imagine that you are not allowed to run more than X copies of some program simultaneously), etc. Here the object to be freed is not an unmanaged resource, but a right to do/to use something, which is a purely inner construct to your program logic.
Small addition: here is a small list of neat examples of [ab]using IDisposable
: http://www.introtorx.com/Content/v1.0.10621.0/03_LifetimeManagement.html#IDisposable.
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