Introduction

This article provides an introduction to destructors in C# and reveals the non-deterministic destructor problem.

It also analyzes the IDisposable, garbage collection, using statement, etc.

Destructor

The destructor is a special purpose method in a class like a constructor. The constructor is called by the runtime after creating an instance. It is meant for initialization purposes. Like that, a destructor is called by the runtime before destroying an instance. It is meant for finalization operations.

A destructor is declared like a constructor with a prefix tilde(~) in C#. A destructor do not have access modifiers like public, private, etc.

publicclass Shape
{
~Shape() // Destructor
{
}
}

In C#, the .NET runtime automatically destroys any class instances that are no longer in reference. It does this during the process of garbage collection.

Using of destructors will make the application slow in performance.

Finalizers

It is important to note that the destructors are also called Finalizers because the compiler converts a C# destructor into a method named Finalize() in the Intermediate Language (IL). The code in the destructor will be wrapped in a try..finally block inside the Finalize() method.

Garbage Collection

This is the process of freeing up of unused memory of objects periodically. The .NET runtime does the process of garbage collection on the following conditions:

When there is shortage of memory

Developer invoked the GC.Collect() method

The garbage collection helps the developer to free up object instances.

But remember, garbage collection only destroys the managed resources, i.e. resources created inside the managed memory.

What is the Need of Destructors?

Since the runtime is managing the destruction of all objects, a question may arise: Why do we need destructors?

Answer: To destroy unmanaged resources.

The unmanaged resources are those created outside the managed environment. They may include file handles, network connections, etc. These resources created should be destroyed by the class itself, because the runtime is not able to destroy them.

Therefore, we use destructors to free up the unmanaged resources. This will ensure that all the unmanaged resources created by the class will be freed in the destructor.

E.g.: Let Channel be a class using unmanaged resources having a constructor and a destructor.

The Channel class locks a file "c:\file.log" in the constructor and unlocks it in the destructor.

Here arises a problem that we cannot predict when the destructor is called. So the file remains locked until the garbage collection, even if the object instance is out of any reference.

For the above problem, we need some method to free the unmanaged resource. In .NET, there is an interface named IDisposable for the purpose.

IDisposable

The IDisposable interface contains a method Dispose() which should be implemented by the class. So we can move freeing up of unmanaged resources to this Dispose() method.

Again there is a problem, that a typical .NET developer not calling the Dispose() method after the usage of class instance. In that case, we need the call to free unmanaged resources both in the destructor and in the Dispose() method.

We can also use a Close() method instead of implementing the IDisposable interface. But, enabling the IDisposable makes our class usable in the using {} statement of C#.

The using statement will automatically call the Dispose() method after exiting the scope of using. (Please refer to the sample code.)

But, it creates a new problem due to the non-deterministic destructors of C#.

Non-Deterministic Destructors

In C#, we do not know when the destructor is executed. It is called only during the garbage collection process and we do not know when it actually happens. This state is called non-deterministic destructors or InDeterministic destructors.

The following figure illustrates the problem. Our 'Channel' class is using some unmanaged resources and freeing them in the Dispose() method and also in the destructor.

There are two cases when the Channel class is used:

A developer creates an instance and exits without calling Dispose()

A developer creates an instance and calls Dispose()

Solution using GC.SuppressFinalize(this)

We can instruct the garbage collector not to call the destructor by using the GC.SuppressFinalize(this) method. So, using this in the Dispose() method after freeing up unmanaged resources will solve the non-deterministic destructor problem.

The solution is given below.

Here, the Channel class has freed up code both in the destructor and in the Dispose() method. And if the developer calls the Dispose() method, the unmanaged resources will be freed and the destructor will be disabled by using GC.SuppressFinalize(this);.

If the developer does not call the Dispose() method, the unmanaged resources are freed up in the destructor by the garbage collector. The figure below illustrates the solution:

Note: The Dispose() method is used to free up the unmanaged resources, and calling Dispose() will not free up the instance. It just executes whatever statements are written in it.

Using the Code

The code along with this article demonstrates the case of using/not using Dispose(), and also the initiating of the GarbageCollection process using GC.Collect(). Please analyze the Channel class to see the details involved.

Actually in the MSDN documentation, so, officially they are called destructors in C# and Managed Extensions fro C++, they're not called finalizers. These destructors however works kind of like proxies for finalizers.

If the Unlock-method would as you say throw an exception you can not call it as you do in the Dispose-method since the Dispose-method should not throw an exception even if called on an already disposed object.

From MSDN: "If an object's Dispose method is called more than once, the object must ignore all calls after the first one. The object must not throw an exception if its Dispose method is called multiple times. Instance methods other than Dispose can throw an ObjectDisposedException when resources are already disposed."

Therefore the problem with non-deterministic destructors could be solved by calling the Dispose-method from the destructor, in fact that's what should be done. However another problem remains, namely that any object of a class that has a destructor defined will never be collected before the second generation of the garbage collector (for reasons that are too long to go into here). That's why a protected (or private if the class is not intended for inheritance) Dispose-method that takes a bool-value indicating if the dispose has been explicitly called should be implemented. Now the public (interface) method Dispose should call the private Dispose with the parameter set to true, while the destructor should call the prive Dispose with the parameter set to false. If the dispose-parameter is true call the GC.SupressFinalize-method, otherwise don't.

None of these descriptions are fully accurate or complete. Forget all of this, and just follow the documented "dispose pattern": http://msdn2.microsoft.com/en-us/library/b1yfkh5e(vs.71).aspx[^]. The pattern is easy to follow, and takes into consideration all of the nuances of finalization. Don't deviate from the pattern (as done here) and you'll be fine, even if you don't understand why it's structured the way it is. Understanding, in this case, is beneficial but not necessary, and an article that only explains half of the issues and presents a broken solution is not useful.

Not sure about that. What does the fact that objects with a finalizer not being collected until gen-2 have to do with anything, especially to do with creating the protected Dispose()? The reason for the protected dispose is because when finalizing you can not reference any other GCed object, since you don't know if they may have been collected already or not.

When the garbage collector is collecting it looks if an object has a finalizer, if it has it is queued for finalizing and will not be collected until the next collection, therefore it's moved up a generation, this means that an object with a finalizer will never be called BEFORE gen-2 it could however live longer than that. This is avoided by calling GC.SupressFinalize from explicit calls to dispose.

You are right, only unmanaged resources should be disposed of from a finalizer thread, that's one of the reasons for the dispose method that takes the "dispose"-paramter. Another reason is to avoid that the object is kept in memory for an extra garbage collection generation (by calling GC.SupressFinalize).

The pattern I described is exactly the one from the article from MSDN you posted a link to. Since that's an article and my message was just intended to point out the errors in this article (and is as you state not complete) anyone intereseted should really check out the article on the Dispose-pattern, that much we can certainly agree on.

I was being serious. We were talking across each other, mostly because I misunderstood your original post. The SuppressFinalize() doesn't go into the protected Dispose(), so I wasn't seeing why you thought the fact that it wouldn't be collected until gen-2 had anything to do with why we use a protected Dispose(). So, I'll agree you were fully accurate, it just wasn't obvious from your wording what you meant .