C#にてClassにDisposeを実装する方法を説明します。
マネジーリソースとアンマネージリソース
リソースの種類は、CLRが管理するマネージリソースとCLRが管理しないアンマネージリソースに分けられる。
マネージリソースは、ガーベージコレクションによって自動的に解放される。
アンマネージリソースは、開発者の責任で解放しなければならない。
※マイクロソフト提供のクラスは、ガーベージコレクションのマネージリソース回収のときに、アンマネージリソースの解放も一緒に行われるようになっている。
自分でアンマネージリソースを扱った実装をするクラスは、
ガーベージコレクションはあくまでマネージリソースしか回収しないので、アンマネージリソースは自分で解放するよう実装しなければならない。
※ガーベージコレクションが回収を開始するのは、マネージリソースが足りなくなったときに限られる。アンマネージリソースが足りなくなっても、ガーベージコレクションは何もしない。なのでアンマネージリソースの解放を怠るとメモリリークが発生する。
IDisposablインターフェイスでDispose()を実装する(基本的な使い方)
class MyClass : IDisposable { public MyClass() { } public void Dispose() { // 終了処理 } }
上記の方法では、Dispose()を使用するのには不足です。
IDisposableインターフェイスの実装目的
1.ガーベージコレクションに頼らず、コード上での明示の解放が必要であることを示す。
2.IDisposableが実装されているということは、クラスがファイル、ストリーム、およびハンドルなどのアンマネージリソースを利用していることを示す。
IDisposableを実装に満たす要件
1.Dispose()では、マネージリソース・アンマネージリソースの解放処理を行う。
2.Dispose()が呼び出されなかった場合、ガーベージコレクションによってマネージリソースの回収が行われる際に、アンマネージリソースの解放するように、デストラクタに実装する。
3.既に解放済みのリソースにもかかわらず、ガベコレの回収によって、デストラクタが呼び出されるとFinalizeキューが処理され、パフォーマンスの低下させるので、GC.SuppressFinalize(obj)を用い、Finalizeの呼び出しが不要なオブジェクトを指定し、デストラクタがガーベージコレクションの対象外となるようにする。
4.Dispose()は複数回呼び出される可能性があるので、複数回呼び出されても問題のないように実装する。
そこで以下のように修正します。
class MyClass : IDisposable { private bool disposed = false; //重複する呼び出しの制御 private void Dispose(bool disposing) { if (disposed) //既に解放済みかチェック return; if (disposing) { //マネージリソースの解放処理(ガベコレによって解放される) } //アンマネージリソースの解放処理(開発者によって解放される) disposed = true; //解放済みフラグを立てる } public virtual void Dispose() { Dispose(true); // Finalizer が呼ばれないようにする。(ガベージコレクションが呼び出されないようにします。) GC.SuppressFinalize(this); } /// <summary> /// デストラクタ /// </summary> ~MyClass() { Dispose(false); } }