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とやってしまいそうになる