IDisposableとusing文
プログラミングC# p86〜87
IDisposableを実装したクラスをusing文と共に作成すると、using文のブロックの終わりで自動的にDisposeメソッドが呼び出されます。
Rubyでいうところの、open関数のブロックみたいなものか?
using System; class DisposeTest : IDisposable { public void hello() { Console.WriteLine("DisposeTest#hello"); } public virtual void Dispose() { Console.WriteLine("DisposeTest#Dispose"); } } class TestDriver { static void Main(string[] args) { using (DisposeTest d = new DisposeTest()) { d.hello(); } } }
※書籍中では、protected virtual void Dispose(bool disposing)という宣言になってますが、現在はpublicで引数無しが正しいようです。
この結果はこう。
DisposeTest#hello DisposeTest#Dispose
というわけで、Dispose呼ばれてますね。
じゃあデストラクタとの兼ね合いはどうか。
デストラクタが呼ばれた場合でも、Disposeが呼ばれた場合でも、なんらかの後始末を行いたいとします。で、こんなコードを書いてみる。
using System; class DisposeTest : IDisposable { public void hello() { Console.WriteLine("DisposeTest#hello"); } public virtual void Dispose() { Console.WriteLine("DisposeTest#Dispose"); destroyProcess(); } ~DisposeTest() { Console.WriteLine("~DisposeTest"); destroyProcess(); } private void destroyProcess() { // なんらかの後始末処理 Console.WriteLine("DisposeTest#destroyProcess"); } } class TestDriver { static void Main(string[] args) { using (DisposeTest d = new DisposeTest()) { d.hello(); } } }
この結果はこう。
DisposeTest#hello DisposeTest#Dispose DisposeTest#destroyProcess ~DisposeTest DisposeTest#destroyProcess
ありゃりゃ。DisposeTest#destroyProcessが二回呼ばれちゃってますね。
これを回避するのが、GC.SuppressFinalize(this);というおまじない。
これはこのメソッドが呼ばれた場合は、デストラクタを呼ばないようにするというワザ(?)です。
using System; class DisposeTest : IDisposable { public void hello() { Console.WriteLine("DisposeTest#hello"); } public virtual void Dispose() { Console.WriteLine("DisposeTest#Dispose"); destroyProcess(); GC.SuppressFinalize(this); } ~DisposeTest() { Console.WriteLine("~DisposeTest"); destroyProcess(); } private void destroyProcess() { // なんらかの後始末処理 Console.WriteLine("DisposeTest#destroyProcess"); } } class TestDriver { static void Main(string[] args) { using (DisposeTest d = new DisposeTest()) { d.hello(); } } }
この結果はこう。
DisposeTest#hello DisposeTest#Dispose DisposeTest#destroyProcess
期待通りですね。