Peter Duniho said:
Ben said:
[...]
BTW the garbage collector won't dispose objects, it will only finalize
them.
Assuming a correctly implemented finalizer, what's the difference? Isn't
the finalizer supposed to dispose the object (at least with respect to
unmanaged resources), by calling Dispose with the appropriate flag?
I don't see the contradiction here. The GC does not call Dispose, that is
exactly why the object needs to properly cleanup the resources in the
finalizer as well as IDisposable.Dispose. If the garbage collector did
dispose objects, you'd hardly ever need a finalizer at all (and the managed
resources would be resurrected, promoted into the next generation).
I also don't understand that comment, for a few somewhat unrelated
reasons:
1) I don't know what sort of race conditions you're talking about.
What code is using the unmanaged resource in a way that is racing with
other code?
Say you're writing a data file for batch processing. If you fail to dispose
the FileStream object, then when you trigger the process to read it, there's
a chance that it will fail because the file is still open by your
application. There's a race between the GC and the next process needing the
file.
Same goes for any other unmanaged resource, except memory. If you don't
explicitly commit the database transaction, your next query may not reflect
the new data. If you don't explicitly release the mutex, the next user may
fail to acquire it. If you don't explicitly close the socket, the TCP FIN
packet isn't sent to the remote end and his read() call may not complete.
Only memory is different, because (1) in almost every case, there is no
persistent reference, it is orphaned, so there isn't anyone waiting to
acquire it next and (2) when system memory runs low, the garbage collector
triggers and starts running finalizers.
2) If the pressure is on unmanaged resources, I would expect failure
to dispose objects to not create the resource pressure that would trigger
a collection. You might get a collection for some other reason, but if
what you're running low on are unmanaged resources then, by definition,
the GC doesn't know about them and won't collect when you run low.
That's true for everything but memory. The GC has to detect shortages of
system RAM and will trigger a collection.
So how does failure to dispose IDisposable objects consistently not
lead to memory issues, and if it doesn't and you aren't writing threaded
code, why is it still important to call Dispose?
For every resource but memory, it leads to a shortage that goes unnoticed by
the GC and isn't self-correcting.
And finally,
3) As the comment relates to your first one...if there's a difference
between finalization and disposing, why does triggering a collection
(assuming one is triggered) address the failure to dispose an object?
There is a difference, but as you already pointed out, properly designed
objects will free the unmanaged resource in either case.