https://msdn.microsoft.com/ja-jp/library/system.idisposable(v=vs.110).aspx
http://toach.click/idisposable/
IDisposableインターフェースを実装するときの書き方のパターンのこと。
C#のusingブロックなどで使われる、終了するときにリソースを開放するクラスを自前実装するときに使う。
やりたいことは「終了するときにリソースを開放する」という単純なことだが、いろいろ問題があってちょっと複雑な書き方が必要になっている。
以下はステップ・バイ・ステップで、どうして複雑になるかの理由を説明する。
1. 素朴な実装
二重に開放されることの対策もしてあるからこれでバッチリだよね!
class DisposableImplement : IDisposable { private bool disposed = false; public void Dispose() { if (disposed == true) { return; } disposed = true; ガベージコレクトされないリソースの開放処理(); ガベージコレクト対象.Dispose(); } }
駄目である。
Disposeメソッドはキーワードでも何でもない普通のメソッドなので、これだと呼び出し側がDispose()を呼ばないときに「ガベージコレクトされないリソースの開放処理」が実行されない。
たとえば、以下のようなコードだとリソースが開放されない。
var obj = new DisposableImplement(); なにかの処理 Application.Exit(); // disposeが呼ばれない
2. だったら、デストラクタを使えばいいね
Dispose()を呼び出し忘れた時も確実にリソース開放をするなら、オブジェクトが破棄されるときに自動的に実行されるデストラクタを使えばOKだね。
これなら、開放時に確実にDispose()が呼ばれるはず。
class DisposableImplement : IDisposable { private bool disposed = false; ~DisposableImplement() { this.Dispose(); } public void Dispose() { if (disposed == true) { return; } disposed = true; ガベージコレクトされないリソースの開放処理(); ガベージコレクト対象.Dispose(); } }
駄目である。
たしかに「ガベージコレクトされないリソースの開放処理」は確実に実行されるようになるが、今度はガベージコレクトされるタイミングでエラーが発生するようになってしまう。
var obj = new DisposableImplement(); なにかの処理 Application.Exit(); // ここでシステムエラー発生 // 「ガベージコレクト対象」は、objのデストラクタが実行されるタイミングでは、既にガベージコレクタによって開放されている。 // だから「ガベージコレクト対象.Dispose();」は、解放済みオブジェクトのメソッド呼び出しエラーになる。
3. そしてDisposeパターンへ…
ならば、デストラクタからDispose処理が実行されたときは「ガベージコレクト対象.Dispose();」を呼ばないように明示的に書く!
そして、Dispose処理がコードから呼び出されたときは、もうそこで開放は終わるからデストラクタは呼び出さない!
これでどうだ!
class DisposableImplement : IDisposable { private bool disposed = false; ~DisposableImplement() { this.DisposeProc(false); } public void Dispose() { this.DisposeProc(true); GC.SuppressFinalize(this); // 指定したオブジェクトに対してデストラクタを呼び出さないようにする } // Dispose処理 private void DisposeProc(bool isDisposing) { if (disposed == true) { return; } disposed = true; ガベージコレクトされないリソースの開放処理(); if (isDisposing == true) { // デストラクタから呼ばれたときは実行されない ガベージコレクト対象.Dispose(); } } }
完成。
説明なしにいきなりこれを見せられると「は?」となるが、こういう経緯で書かれているのが「Disposeパターン」である。
これならば、Disposeを呼び出し忘れた時もリソースは開放されるし、解放済みオブジェクトのメソッド呼び出そうとしてエラーが出ることもない。
上のMSDNで「使用例」として書かれているコードがこのパターンを使った例になっている。