A destructor is a member method in a C# class that implements the actions required to destruct an instance of a class. The syntax for writing a destructor is a tilde (~) followed by the name of the class in which the desctructor is declared. For example, the following C# code defines its destructor for A class:
using System;
class A
{
~A()
{
Console.WriteLine("Destroying A");
}
}
Unlike other programming languages, there are some very important restrictions that apply to destructors in C#. The following sections list all these restrictions and explain why.
Every class can only have one destructor
Since a destructor is required to have no parameters, it cannot be overloaded, so a class can have, at most, one destructor.
The destructor cannot be declared an access modifier (such as public) and it is not inherited.
An interface cannot contain destructor
The members of an interface must be methods, properties, events, or indexers. An interface cannot contain constants, fields, operators, instance constructors, destructors, or types, nor can an interface contain static members of any kind.
A struct is not permitted to declare a destructor
A struct is a value type that lives on the stack and not the heap, so garbage collection does not apply.
struct A
{
~A() { ... } // compile-time error
}
Destructor is invoked automatically and cannot be invoked explicitly
An instance becomes eligible for destruction when it is no longer possible for any code to use that instance. When an instance is destructed, the destructors in that instance’s inheritance chain are called, in order, from most derived to least derived. The output of the example
using System;
class A
{
~A()
{
Console.WriteLine("Destroying A");
}
}
class B: A
{
~B()
{
Console.WriteLine("Destroying B");
}
}
class Test
{
static void Main()
{
B b = new B();
b = null;
//Forces an immediate garbage collection
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
is
Destroying B
Destroying A
Execution of the destructor for the instance may occur at any time after the instance becomes eligible for destruction, that means the timing of destructor invocations is not deterministic, and destructors may be executed on any thread. For these and other reasons, classes should implement destructors only when no other solutions are feasible.
Exception handling in destructor
Exceptions that occur during destructor execution are worth special mention. If an exception occurs during destructor execution, and that exception is not caught, then the execution of that destructor is terminated and the destructor of the base class (if any) is called. If there is no base class (as in the case of the object type) or if there is no base class destructor, then the exception is discarded.
Destructor is implemented by overriding the virtual method Finalize on System.Object
C# programs are not permitted to override this method (Finalize()) or call it directly. For instance, the program
class A
{
override protected void Finalize() {} // error
public void F()
{
this.Finalize(); // error
}
}
contains two errors.
Internally, the compiler automatically translates a destructor into an override of the Object.Finalize method. The compiler translates the following destructor:
class A
{
~A()
{
//clearnup statements...
}
}
Into this:
class A
{
protected override void Finalize()
{
try
{
//clearup statements...
}
finally
{
base.Finalize();
}
}
}