正しいDisposeの実装方法

C#でクラスのリソース開放をする時、普通はDisposeメソッドを実装する。
このDisposeだがあまりC#詳しくない人だとてきとうに実装してしまいがち。
こんな感じ。↓

// ダメなDisposeの実装方法
internal class MySampleClass
{
    private SqlConnection _connection = new SqlConnection();

    public void Dispose()
    {
        // ここでリソース開放する
        _connection.Dispose();
        _connection = null;
    }
}

この実装でも呼び出し元でクラスを使い終わった後に必ずDisposeを呼び出すなら問題は無い。
ただC#だとusingステートメントというスコープで囲むことで、スコープを外れたときに自動でDisposeを呼び出してくれる便利な機能がある。
この機能を使えるようにしておいた方がよいのでIDisposableインターフェースを実装しておこう。

// 良いDisposeの実装方法
internal class MySampleClass : IDisposable
{
    private SqlConnection _connection = new SqlConnection();

    public void Dispose()
    {
        // ここでリソース開放する
        _connection.Dispose();
        _connection = null;
    }
}

こう実装しておくことでクラスを使用する際にusingステートメントが使えてDisposeをあえてコールしなくてもよいのでラク。

using (MySampleClass sample = new MySampleClass())
{
    // 何か処理をする
}

マネージコードだけを使っているクラスの場合はこれで十分だが、Win32API等アンマネージコードを使うようなクラスの場合はもっと正しくDisposeを実装する必要がある。
マネージコードについてはusingを使わずDisposeの呼び出しを忘れたとしても、GCが動いた際に自動で開放されるので最悪問題は無い。だがアンマネージコードについてはGCで自動開放されないのでメモリリーク等の原因になる。

そのためこんな感じで実装する必要がある。

// アンマネージドコードを使う場合の良いDisposeの実装方法
internal class MySampleClass : IDisposable
{
    private bool _disposed;
    private SqlConnection _connection = new SqlConnection();

    // ファイナライザ
    ~MySampleClass()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                //ここでマネージドリソースの開放をする
                _connection.Dispose();
                _connection = null;
            }
            // ここでアンマネージドリソースの開放をする
            _disposed = true;
        }
    }
}

ファイナライザを実装することでDisposeの呼び出しを忘れた場合でも、クラスの使用が終わった際に強制的にDisposeが呼ばれるようにする。