Daniel,
"Automatic memory management is one of the services that the common language
runtime provides during Managed Execution. The common language runtime's
garbage collector manages the allocation and release of memory for an
application. For developers, this means that you do not have to write code
to perform memory management tasks when you develop managed applications.
Automatic memory management can eliminate common problems, such as
forgetting to free an object and causing a memory leak, or attempting to
access memory for an object that has already been freed."
How true. Note however, that this statement is only about *memory*
management. Finalize() (written as destructor in C#) and Dispose() are
about *resource* management.
When you scan through the .NET framework you'll find 4 different types
of classes implementing IDisposable:
1. Classes where cleanup affects more than one object with a
finalizer, e.g. StreamWriter (A StreamWriter object internally holds a
reference to a Stream object and both have a finalizer).
2. Classes representing non-shareable resources, e.g. FileStream
3. Classes representing resources that are limited by other factors
than memory, e.g. Icon (the number of Icons an AppDomain can create
seems to be limited to ~5000).
4. Others implement it to put the object in a state where it is no
longer used, e.g. MemoryStream.Dispose() marks the stream as closed
but does nothing else.
a. When you fail to call Dispose() (or any other method that cleans up
like e.g. Close()) on objects of classes similar to 1, you are
guaranteed to lose data. This is because the finalization order of no
longer accessible objects is not defined in .NET. Consequently
StreamWriter.~StreamWriter cannot access the Stream object because its
finalizer might have run already.
b. When you fail to call Dispose() on an object similar to 2, you will
*often* run into a problem. I.e. when you try to use the resource
again later. E.g. create a FileStream object, write to the file and
let the object go out of scope. A few seconds later try to do the same
again. Unless you've been very lucky (i.e. the GC has run in the mean
time), the second attempt will fail, as the first object is still
sticking around having a write lock on the file.
c. When you fail to call Dispose() on objects similar to 3, you will
*sometimes* run into problems. I.e. when you use a large enough number
simultaneously and have a high create/throw-away rate.
d. When you fail to call Dispose() on objects similar to 4, you'll
almost never run into problems.
If you want your code do the right thing you have two strategies:
A. Always call Dispose() on objects implementing IDisposable
B. Check documentation what kind of class you have (1 .. 4) and call
Dispose only if necessary
You decide what's best ;-) ...
Regards,
Andreas
P.S. I personally went from A to B and back to A (and I think I stick
with A for the rest of my carreer). The last move is due to a
discussion with John Skeet followed by a deep of research into the
subject.