Ok.. prepare for the long text...
Finalizer is what you write to execute by the GC before releasing the
object's memory, destructor is... wait, we don't write destructors in
C#. A destructor is meant to free objects memory, just as the
constructor allocates the new object the destructor releases it, but
sinces you can't explicitly destroy an object in C# (that's handled by
the GC) then you can say that the destructor is GC's "private
property". Consider the difference between the finalizer and the
destructor as the difference between the initializer (whichever method
sets up the initial values of the fields of the object) and the
constructor... I know it becomes confusing since in C# constructor and
initialize is often the same method. That is because in C# the fields
will have a value, there wont be trash values as happens in C++. But
take the example of a struct, structs doesn't allow to define the
default constructor, that default constructor sets all the values to
default, and then you need to set the vaules you want (unless you have
more constructors ^_^), setting those values is like the initializer.
Now, in analogy the finalizer releases anything the object have
references or allocated that the GC is not aware or that needs an
special tratement, and then the destructor releases the memory of the
object.
Finalizers are sometimes necesary, but in general a dispose is
preferible, an interesting aproach to finalizers is to write the
finalizer to call the dispose, and write the dispose to suppress the
finalizer (that's to call GC.SuppressFinalize method), that way if the
dispose method was called (as it should be) the finalizer won't run
(assuming the dispose method took care of the work), but if because of
an error, poorly handled exception, poor programming, or just a
mistake the dispose method didn't get called the finalizer will do the
task.
WARNING: Resurrecting objects is posible, if you add a reference to
the object from it's finalizer, but if the finalizer was called (or
supressed), it won't be called again by the GC, unless you explicitly
call Re Regisrer for Finalize.
RE-WARNING: Do not, never, ever, call Re Register For Finalize on an
more times than those it's Finalizer has been called or supressed, In
general, The object can be registered for finalization at maximun
once.
Now why finalizers are not that good? well because the GC (until .NET
3.5) needs to stops all the threads to run a garbage collection, and
the finalizer is going to delay the execution of that collection, so
it will hit overall performance, also a bad programmed finalizer (such
as one that throws an exception) may end in very hard to debug
behavior. Now that said, it happens that the GC is free to suddently
stop bad behaving finalizers or those that takes too long to execute,
and may be, you need to make sure that the finalizer executes for
whatever reason, you can inherit from (WARNING!)
CritialFinalizerObject.
WARNING: a deadlock, an inifinite loop, and outofmemoryexception
threadabortexception and stackoverflowexception, are things you will
have to overcheck in your finalizer, as a general way of doing things,
avoid writting any logic directly in the finalizer (that is, no if, no
while, no for, no goto, no switch, in the finalizer), instead if you
really, really need any of this, place it in another method (such as
the dispose) so you can debug that in case the worst happens.
Now, me, in my war against software ignorance, I will allow you see a
class I'm using for my own good, it's based on some code from
CodeProject (can't recall the exact one...), but it takes care of this
for general cases, and if your case is not enoughly general to use
this, perhaps you have done another wrong design descition.
Make sure to read and understand before use. Make sure to test it
before any modification.
//[code begin] ThreadSafeDisposable.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Theraot
{
public class ThreadSafeDisposable : IDisposableEx
{
public ThreadSafeDisposable()
{
_disposeSyncRoot = new object();
}
~ThreadSafeDisposable()
{
Dispose(false);
}
protected void CheckDisposed()
{
if (_IsDisposed)
throw new ObjectDisposedException(GetType().FullName);
}
#region Miembros de IDisposableEx
protected volatile object _disposeSyncRoot;
private volatile bool _IsDisposed;
public bool IsDisposed
{
get { return _IsDisposed; }
}
#endregion
#region Miembros de IDisposable
public void Dispose()
{
lock (_disposeSyncRoot)
{
if (_IsDisposed)
{
return;
}
_IsDisposed = true;
}
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
}
#endregion
}
}
//[code begin] IDisposableEx.cs
using System;
namespace Theraot
{
public interface IDisposableEx : IDisposable
{
bool IsDisposed { get; }
}
}
//[code end]
To use disposeSyncRoot and CheckDisposed follow this pattern inside
your methods:
//Initlialization, here declare locals, copy parametter's
values, cache what's applicable, and use any method that doesn't
require syncronization if any.
lock (base._disposeSyncRoot)
{
base.CheckDisposed();
//actual sync'd code
}
//post lock execution, for example if you did a local copy
of the resource you are protecting (the same resource you free in the
dispose method*), you can work on the copy, as it's local only.
return something; //returning whatever you have to return
if any.
*In addition to this, you will want to override the dispose method and
put your cleanup code there, the paramether will be true if the call
come from a dispose and false if it's the finalizer, it may be
important if you have any thread specific code (may be use use GUI
objects, or a Thread Static value). Otherwise, just clean up.
Also some times you may come to the case where it's preferible to use
Monitor.TryEnter instead of a lock, but I'll not describe that
situation here.
Apart form this code, is ok to have others sync objects to lock
against, but use this one for those method that depend on whatever you
free on dispose, so they can throw an ObjectDisposedException from
CheckDisposed.
I will recomend you to have a look on the generational algorithm
the .NET GC uses, and also on writing thread safe code, with
techniques such as Volatile, Monitor (lock included), Mutex, Thread
Static (attribute), and Invoke and BeginInvoke for GUI.
If you got extra time: File Locking, Auto / Manual Reset Events,
Semaphore, Syncrhonization Attribute.
Whoa, you still want more? well, what about thread pools and queues,
timers and memory barriers?.
Al. J. Ramos
On 4 mar, 00:34, Sue <[email protected]> wrote:
> Hi,
>
> I am new to c#. Can anyone tell me what's the difference in C# between
> destructor and finalizer? Thanks.