IDisposable

C#ではICloneableなのにIDisposableなのか。
というのは置いといて、C++のデストラクタに近いのがDisposeメソッド。このメソッドはIDisposableインターフェイスのメソッドで、using文と併用することでRAIIなクラスが作れる。

class Program : IDisposable
{
    private static int count = 0;
    private readonly int id = count++;

    public void Dispose()
    {
        System.Console.WriteLine("dispose {0}", id);
    }

    static void Main(string[] args)
    {
        using (Program p1 = new Program())
        using (Program p2 = new Program())
        {
            System.Console.WriteLine("proc");
        }
        System.Console.WriteLine("end");
    }
}

こうすると、


proc
dispose 1
dispose 0
end

と表示される。


で、ちょっと気になった箇所が。

型によっては、使用する側のコードからClose()を呼んでもらうほうが適当な場合があります(例えば、ファイルにはDispose()よりもClose()が適当です)。Dispose()はprivateにしておいて、それをpublicなClose()メソッドから呼ぶように書けばよいでしょう。

プログラミングC# 4.4.3 Closeメソッド

・・・privateなDisposeをpublicなCloseから呼ぶ?そんなことができるのか???
ということで試してみる。

// publicからprivateに
private void Dispose()
{
    System.Console.WriteLine("dispose {0}", id);
}

// Closeメソッドを追加
public void Close()
{
    Dispose();
}


'Program' はインターフェイス メンバ 'System.IDisposable.Dispose()' を実装しません。
'Program.Dispose()' は public ではないため、インターフェイス メンバを実装できません。

まぁ、そうだよな。


で、これをJavaでやる場合はもちろんtry-finallyで。

class Program {
    private static int count = 0;
    private final int id = count++;

    public void dispose() {
        System.out.printf("dispose %d%n", id);
    }

    public static void main(String[] args) {
        Program p1 = null;
        Program p2 = null;
        try {
            p1 = new Program();
            p2 = new Program();
            System.out.println("proc");
        } finally {
            if (p2 != null) p2.dispose();
            if (p1 != null) p1.dispose();
        }
        System.out.println("end");
    }
}

とめも面倒。さらに、disposeメソッドが例外を投げる可能性があったりすると更に複雑に。
IDisposable*1とusing文はその点便利・・・なんだけど、using文の中でtry-catchを書かなければいけなかったりするとインデント+1なわけで・・・
こういうところはC++がいい感じだよなぁ。

*1:VSを使ってるから問題ないけど、どうしてもIDisposeableとやってしまいそうになる