C#にてClassにDisposeを実装する方法を説明します。

マネジーリソースとアンマネージリソース

リソースの種類は、CLRが管理するマネージリソースとCLRが管理しないアンマネージリソースに分けられる。

マネージリソースは、ガーベージコレクションによって自動的に解放される。

アンマネージリソースは、開発者の責任で解放しなければならない。

※マイクロソフト提供のクラスは、ガーベージコレクションのマネージリソース回収のときに、アンマネージリソースの解放も一緒に行われるようになっている。

自分でアンマネージリソースを扱った実装をするクラスは、

ガーベージコレクションはあくまでマネージリソースしか回収しないので、アンマネージリソースは自分で解放するよう実装しなければならない。

※ガーベージコレクションが回収を開始するのは、マネージリソースが足りなくなったときに限られる。アンマネージリソースが足りなくなっても、ガーベージコレクションは何もしない。なのでアンマネージリソースの解放を怠るとメモリリークが発生する。

IDisposablインターフェイスでDispose()を実装する(基本的な使い方)

class MyClass : IDisposable {
  public MyClass()
  {
  }
  public void Dispose() {
    // 終了処理
  }
}

上記の方法では、Dispose()を使用するのには不足です。

IDisposableインターフェイスの実装目的

1.ガーベージコレクションに頼らず、コード上での明示の解放が必要であることを示す。

2.IDisposableが実装されているということは、クラスがファイル、ストリーム、およびハンドルなどのアンマネージリソースを利用していることを示す。

IDisposableを実装に満たす要件

1.Dispose()では、マネージリソース・アンマネージリソースの解放処理を行う。

2.Dispose()が呼び出されなかった場合、ガーベージコレクションによってマネージリソースの回収が行われる際に、アンマネージリソースの解放するように、デストラクタに実装する。

3.既に解放済みのリソースにもかかわらず、ガベコレの回収によって、デストラクタが呼び出されるとFinalizeキューが処理され、パフォーマンスの低下させるので、GC.SuppressFinalize(obj)を用い、Finalizeの呼び出しが不要なオブジェクトを指定し、デストラクタがガーベージコレクションの対象外となるようにする。

4.Dispose()は複数回呼び出される可能性があるので、複数回呼び出されても問題のないように実装する。

そこで以下のように修正します。

class MyClass : IDisposable {

    private bool disposed = false;      //重複する呼び出しの制御

    private void Dispose(bool disposing)
    {
        if (disposed)                   //既に解放済みかチェック
            return;

        if (disposing)
        {
            //マネージリソースの解放処理(ガベコレによって解放される)
        }

        //アンマネージリソースの解放処理(開発者によって解放される)

        disposed = true;                //解放済みフラグを立てる
    }

    public virtual void Dispose()
    {
        Dispose(true);

        // Finalizer が呼ばれないようにする。(ガベージコレクションが呼び出されないようにします。)
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// デストラクタ
    /// </summary>
    ~MyClass()
    {
        Dispose(false);
    }
}