web analytics

Understanding the Destructor in C#

Options

codeling 1599 - 6654
@2015-12-19 20:43:51

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(); 
        }
    }
}
@2015-12-20 11:41:18

What causes C# finalize method to be called?

In C#, finalize methods are called at the completion of a garbge collection, which is started by one of the following five events:

  1. Generation 0 is full. When generation 0 is full, a garbage collection starts. The event is by far the most common way for Finalize methods to be called because it occurs naturally at the application code runs, allocating new objects.
  2. Code explicitly calls System.GC's static Collect method. Code can explicitly request a garbage collection by calling System.GC's static Collect method, although Microsoft strongly discourages such request.
  3. Windows is reporting low memory conditions. The Common Language Runtime (CLR) internally uses the Win32 CreateMemoryResourceNotification and QueryMemoryResourceNotification functions to monitor system memory overall. If Windows reports low memory, the CLR will force a garbage collection in an effort to free up dead objects to reduce the size of a process working set.
  4. The Common Language Runtime (CLR) is unloading an AppDomain. When an AppDomain unloads, the CLR considers nothing in the AppDomain to be a root, and a garbage collection consisting of all generations is performed.
  5. The Common Language Runtime (CLR) is shutting down. The CLR shut down when a process terminates normally. During this shutdown, the CLR considers nothing in the process to be a root and calls the Finalize method for all objects in the managed heap.

The CLR uses a special, dedicated thread to call Finalize methods. For the first four events, if a Finalize method enters an infinite loop, this special threda is blocked, and no more Finalize methods can be called. This is a very bad situation because the application will never be able to reclaim the memory occupied by the finalizable objects - the application will leak memory as long as it runs.

For the fifth event, each Finalize method is given approximately 2 seconds to return. If a Finalize method doesn't return within 2 seconds, the CLR just kills the process - no more Finalize methods are called. Also, if it takes more than 40 seconds to call all objects' Finalize methods, again, CLR just kills the process.

@2015-12-20 12:02:37

Can I Override the Finalize Method in My C# Application?

You cannot override the Finalize method in the C#, instead, you have to use destructor syntax to implement the Finalize method.

A Finalize method acts as a safeguard to clean up resources in the event that your Dispose method is not called. You should only implement a Finalize method to clean up unmanaged resources. You should not implement a Finalize method for managed objects, because the garbage collector cleans up managed resources automatically. By default, the Object.Finalize method does nothing. If you want the garbage collector to perform cleanup operations on your object before it reclaims the object's memory, you must override this method in your class.

For example, the following is a declaration of a destructor for the class Class1:

class Class1
{
    ~ Class1()  // destructor
    {
        // cleanup statements...
    }
}

The destructor implicitly calls Finalize on the object's base class. Therefore, the preceding destructor code is implicitly translated to:

protected override void Finalize()
{
    try
    {
        // cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}

This means the Finalize method is called recursively for all of the instances in the inheritance chain, from the most-derived to the least-derived.

 

@2015-12-21 20:21:26

Finalize vs Dispose in C#

It is not legal to call a destructor explicitly. Your destructor will be called by the garbage collector. If you do handle precious unmanaged resources (such as file handles) that you want to close and dispose of as quickly as possible, you ought to implement the IDisposable interface. The IDisposable interface requires its implementers to define one method, named Dispose(), to perform whatever cleanup you consider to be crucial. The availability of Dispose() is a way for your clients to say, "Don’t wait for the destructor to be called; do it right now."

If you provide a Dispose() method, you should stop the garbage collector from calling your object’s destructor. To stop the garbage collector, you call the static method, GC.SuppressFinalize(), passing in this reference for your object. Your destructor can then call your Dispose() method. Thus, you might write:


	using System;
	class Testing : IDisposable
	{
	  bool is_disposed = false;
	  protected virtual void Dispose(bool disposing)
	  {
	    if (!is_disposed) // only dispose once!
	    {
	      if (disposing)
	      {
		Console.WriteLine("Not in destructor, OK to reference
	other objects");
	      }
	      // perform cleanup for this object
	      Console.WriteLine("Disposing...");
	    }
	    this.is_disposed = true;
	  }
	  public void Dispose()
	  {
	    Dispose(true);
	    // tell the GC not to finalize
	    GC.SuppressFinalize(this);
	  }
	  ~Testing()
	  {
	    Dispose(false);
	    Console.WriteLine("In destructor.");
	  }
	}

 

Implementing the Close method

For some objects, you’d rather have your clients call the Close() method. (For example, Close makes more sense than Dispose() for file objects.) You can implement this by creating a private Dispose() method and a public Close() method and having your Close() method invoke Dispose().

 

Comments

You must Sign In to comment on this topic.


© 2024 Digcode.com