Close, but there are some important inaccuracies.
First I'd recommend anyone that doesn't clearly understand GC and
IDisposable to read both "Effective C#" by Bill Wagner for an excellent,
clear explanation and (for C# developers) "CLR via C#" by Jeffrey
Richter For all the gory details. "Framework Design Guidelines" by
Cwalina & Abrams is also a great resource.
A couple of points from below though:
1. In C# the finalizer is ~ClassName() and it should not be called the
destructor. Note that managed C++ has a destructor and a finalizer -
they are different things. If you are doing C# development you should
never let the word destructor pass your lips. This point is clearly made
by the .NET Framework team in "Framework Design Guidelines".
2. As David Kean hinted, you should very rarely need a finalizer these
days. Finalizers should *only* be used to clean up *unmanaged*
resources. As David points out, if you use SafeHandles to interact with
your unmanaged resources then you aren't directly using unmanaged
resources (the SafeHandle is doing it for you) so you don't need a
finalizer.
3. Implementing IDisposable allows for *deterministic* cleanup (like
destructors in C++). However, unlike C++, in C# you are relying on the
client code correctly calling Dispose to ensure deterministic clean up,
whereas it is guaranteed in C++. Finalizers are required for the case
where the client code has a bug and doesn't call Dispose() but the
object controls an unmanaged resource. The GC has no way of knowing
about unmanaged resources so the finalizer is the solution to prevent
leaking of unmanaged resources even if the client code doesn't call
Dispose().
4. If you forget to call Dispose() on an object that doesn't control
unmanaged resources (and hence doesn't have a finalizer) then the impact
of that is that the object will survive for a little longer before it is
non-deterministically cleaned up by the GC because it is unreferenced.
5. You should only dispose an object that you *know* you will never use
again. I can't really understand how a project can use a value again
later when it shouldn't have. GC will only collect objects that are
unreferenced. Note that you can detect this problem easily by adding a
boolean _isDisposed field to your class, setting it to true in the
Dispose(bool) method and checking in each public method and property
that the field is false and if not throwing an ObjectDisposed exception.
That way you will find these sorts of issues asap.
6. While I agree that using() is preferable to try/finally (when
applicable), it is actually just semantic sugar and produces exactly the
same IL code as a try/finally block. Using() looks nicer and is easier
to read, but (as far as I understand) is no slower or faster than
try/finally. That is:
using(Foo f = new Foo())
{
// whatever
}
is identical to:
Foo f = null;
try
{
f = new Foo();
// whatever...
}
finally
{
if(f != null)
f.Dispose();
}
7. You say as a matter of course you add the code below to all classes
you create. This is a bad idea for two reasons (a) it includes a
finalizer (b) by making your class implement Idisposable you are now
making all classes derived from your class, or that contain an instance
of your class Idisposable. You should avoid making a class disposable if
there is no reason to.
Cheers,
Ben
-----Original Message-----
From: [email protected]
[mailto:[email protected]] On Behalf Of Glen Harvy
Sent: Tuesday, 15 June 2010 5:01 PM
To: [email protected]
Subject: Re: Implement IDisposable?
I've never understood much of the theory either however I have had some
bad experiences with not implementing IDisposable and adding the bellow
mentioned code to all my classes. That is, the GC does everything when
it want's to ie when it has time to do it - it is not top priority. That
means that unless you specifically dispose/destruct an object, it will
remain in memory for at least some time. That fact alone caused me grief
when my project later used that value when it shouldn't have. Again - it
was my fault it was there but I just assumed the GC would have disposed
it already.
The argument has been put that the GC will do it and Finalizers (are
they the same as Destructors - I don't think so) handle it for you so
why even bother. Ok - why bother even having Finalizers and Destructors
if the GC will do it. I chose the opposite - your code is the most
important thing now-a-days, not disk space or memory usage, so therefor
what have you got to lose by properly implementing a Dispose method.
Using users (using()) is far preferable than tcf in my opinion. I
understand that using dispose within a tcf block adds the dispose call
to the GC stack. In other words, there will be a time delay in having it
really destructed (really disposed). using() on the other hand
apparently calls the destructor immediately.
In a VS scenario, the Dispose is included in the designer.cs code page
because each control created by the designer is placed into a container
control. When the form is closed, the container is disposed of by
calling the dispose method and that's why putting the dispose method in
the designer.cs file makes sense. However, I move this code to my .cs
file if I need to specifically dispose of an object in my code ( this is
rare nowadays because I use using() almost all the time.)
As a matter of course, that is whenever I create a class I add the
following at the end:
#region Dispose
/// <summary>
/// Performs application-defined tasks associated with freeing,
releasing, or resetting unmanaged resources.
/// </summary>
///
~*****() //<---- This is the Destructor ... Put the name of you
Class where the ***** appears.
// The tilde ~ is the actual destructor
indicator
{
// In case the client forgets to call
// Dispose , destructor will be invoked for
Dispose(false);
}
public void Dispose()
{
Dispose(true);
// Ensure that the destructor is not called
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed
and unmanaged resources; <c>false</c> to release only unmanaged
resources.</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Free managed objects.
}
// Free unmanaged objects
// Free your own state (unmanaged objects).
// Set large fields to null.
}
#endregion
This works for me and I'm most lilely not going to change anything but
if someone wants to tell me what damage Idoing to my computer I'm happy
to listen :-)
Glen.
This email is intended for the named recipient only. The information it
contains may be confidential or commercially sensitive. If you are not the
intended recipient you must not reproduce or distribute any part of this email,
disclose its contents to any other party, or take any action in reliance on it.
If you have received this email in error, please contact the sender
immediately and delete the message from your computer.