Destroying Objects

Because C# provides garbage collection, you never need to explicitly destroy your objects. However, if your object controls unmanaged resources, you will need to explicitly free those resources when you are done with them. Implicit control over unmanaged resources is provided by a finalizer, which will be called by the garbage collector when your object is destroyed.

The finalizer should only release resources that your object holds on to, and should not reference other objects. Note that if you have only managed references, you don't need to and should not implement a finalizer; you want this only for handling unmanaged resources. Because there is some cost to having a finalizer, you ought to implement this only on methods that require it (i.e., methods that consume valuable unmanaged resources).

You can't call an object's finalizer directly. The garbage collector will call it for you.

Finalizers Versus Dispose

It is not legal to call a finalizer explicitly. Your finalizer 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.[7] (You will learn more about interfaces in Chapter 8.) 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 finalizer to be called, do it right now."

If you provide a Dispose( ) method, you should stop the garbage collector from calling your object's finalizer. To do so, call the static method GC.SuppressFinalize( ), passing in the this pointer for your object. Your finalizer 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 finalizer, 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 finalizer.");
  }
}

Implementing the Close( ) Method

For some objects, you may prefer to have your clients call a method named Close( ). (For example, Close( ) may make 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( ).

The using Statement

To make it easier for your clients to properly dispose of your objects, C# provides a using statement that ensures that Dispose( ) will be called at the earliest possible time. The idiom is to declare the objects you are using and then to create a scope for these objects with curly braces. When the closing brace is reached, the Dispose( ) method will be called on the object automatically, as illustrated in Example 4-7.

Example 4-7. The using statement

using System.Drawing;

namespace usingStatement
{
  class Tester
  {
    public static void Main(  )
    {
      using ( Font theFont = new Font( "Arial", 10.0f ) )
      {
        // use theFont
      } // compiler will call Dispose on theFont

      Font anotherFont = new Font( "Courier", 12.0f );

      using ( anotherFont )
      {
        // use anotherFont
      } // compiler calls Dispose on anotherFont
    }
  }
}

In the first part of this example, the Font object is created within the using statement. When the using statement ends, Dispose( ) is called on the Font object.

In the second part of the example, a Font object is created outside the using statement. When we decide to use that font, we put it inside the using statement; when that statement ends, Dispose( ) is called once again.

This second approach is fraught with danger. If an exception is thrown after the object is created, but before the using block is begun, the object will not be disposed. Second, the variable remains in scope after the using block ends, but if it is accessed, it will fail.

The using statement also protects you against unanticipated exceptions. Regardless of how control leaves the using statement (including you placing a return statement in the middle of the block), Dispose( ) is called. An implicit try-finally block is created for you. (See Chapter 11 for details.)



[7] * Most of the time, you will not write classes that deal with unmanaged resources such as raw handles directly. You may, however, use wrapper classes such as FileStream and Socket, but these classes do implement IDisposable, in which case, you ought to have your class implement IDisposable (but not a finalizer). Your Dispose method will call Dispose on any disposable resources that you're using.

Get Programming C# 3.0, 5th Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.