デストラクタ

デストラクタの要件

  • クラス以外では、定義できない
  • 1つのクラスでは、複数の定義はできない
  • 継承やオーバーロードはできない
  • 明示的に呼び出せない
  • 修飾子や引数を持てない
Remarks - Destructors (C# Programming Guide) | MSDN

ファイナライザ (finalizer)

class Example
{
    ~Example() // デストラクタ
    {
        // 終了処理
    }
}

デストラクタの構文は、実際には次のようにFinalize()の呼び出しとなります。

protected override void Finalize() // ファイナライザ
{
    try
    {
        // 終了処理
    }
    finally
    {
        base.Finalize();
    }
}

ファイナライザを使用しない方法

ファイナライザを使用しない方が良い理由。

  • ファイナライザが呼び出されるタイミングを制御ず、呼び出されないこともある。
  • 複数のオブジェクトのファイナライザが呼び出される順番を予測できない。
  • ファイナライザを持つオブジェクトがほかのオブジェクトを参照している場合、参照先のオブジェクトの寿命が不要に引き延ばされる。
  • ファイナライザを持つオブジェクトは、マネージヒープに割り当てるときに時間がかかる。
C#エッセンシャルズ 第2版 3.12.3「ファイナライザ」

サンプルコード

class Example : System.IDisposable
{
    private bool disposed = false;

    ~Example()
    {
        Dispose( false );
    }

    public void Dispose()
    {
        Dispose( true );

        // このインスタンスのファイナライザが呼び出されないようにする
        System.GC.SuppressFinalize( this );
    }

    protected virtual void Dispose( bool disposing )
    {
        if( !this.disposed )
        {
            if( disposing )
            {
                // (引数なしのDispose()から呼ばれたとき、ここが実行される)
                // マネージ リソースを解放する
            }

            // アンマネージ リソースを解放する

            this.disposed = true;
        }
    }
}
IDisposable インターフェイス (System) | MSDN GC.SuppressFinalize メソッド (System) | MSDN

ガベージコレクション (Garbage Collection)

Dispose()メソッド

明示的にリソースを解放する必要があるならば、IDisposableインターフェイスを実装します。そしてそのインターフェイスを実装しているクラスのオブジェクトは、Dispose()メソッドにより解放できます。IDisposable インターフェイス (System) | MSDN

void Dispose()
IDisposable.Dispose メソッド (System) | MSDN

リソースの解放やパフォーマンスの向上が必須でなければ、Dispose()を呼び出すべきではありません。

usingステートメント (using Statement)

usingの対象とできる型は、IDisposableを継承したクラスだけです。Compiler Error CS1674 | MSDN

using (FileStream fs = new FileStream("sample.txt", FileMode.Create))
{
    // オブジェクトを使用した処理
}

usingステートメントはDispose()を確実に呼び出すための簡易構文に過ぎず、コンパイル時には次のように展開されます。

{
    FileStream fs = new FileStream("sample.txt", FileMode.Create);
    try
    {
        // オブジェクトを使用した処理
    }
    finally
    {
        if (fs != null)
        {
            ((IDisposable)fs).Dispose();
        }
    }
}

複数のインスタンスの宣言

複数のインスタンスが必要ならば、次のようにネストして記述できます。

using (ResourceType r1 = e1)
{
    using (ResourceType r2 = e2)
    {
    }
}

これは次のようにも記述できます。

using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
{
}

またはカンマ区切りで記述しても同じです。

using (ResourceType r1 = e1, r2 = e2)
{
}

参考

参考書

オブジェクトの破棄

基本的にガベージ コレクタが自動で破棄するため、明示的な処理は不要です。しかし必要ならば、Dispose()を呼びます。

obj.Dispose();

もし対象のオブジェクトがIDisposableを実装しておらず、Dispose()を呼べないならば

obj = null;

とします。

トラブル対処法

CA2213

コード分析の実行により、「'A' は、IDisposable 型 'B' であるフィールド 'A.B' を含んでいます。このフィールドで Dispose または Close を呼び出すには、'A' の Dispose メソッドを変更してください。」と警告されるときには、クラスAのDispose()内で、フィールドBのDispose()を呼び出すようにします。

この警告はIDisposableを継承したクラスが、同様にIDisposableを継承したクラスをフィールドに持つとき、Dispose()メソッドでフィールドのDispose()を呼び出していない場合に発生します。CA2213: 破棄可能なフィールドは破棄されなければなりません | MSDN

MSDN (Microsoft Developer Network) から検索