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);
}
}