class Foo: IDisposable
{
// アンマネージリソース
private IntPtr unmanaged;
// IDisposable を実装している他のクラス(このクラスがオーナー)
private OtherDisposableClass other;
// イベントを持ってて、このクラスがイベントをサブスクライブしてる
// このクラスは参照してるだけ
private ReferencedWithEvent refwe;
// 自分のイベント
public event EventHandler AnyEvent;
// もし、アンマネージリソースをこのクラスが直接管理していなければ、
// ファイナライザを用意しなくていい。
~Foo()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SupressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing) {
// Dispose() から呼び出された時は、保持しているIDisposableメンバを
// null チェックしてから Dispose() を呼び出して null にセットしておく。
// 他の例にあるような disposed フラグは使わない。
// その理由は、other のインスタンスが Open() などのように
// コンストラクタ外で確保される場合でも同じパターンでコードを書くため。
// また、null チェックして null 代入しておけば Dispose() が複数回コールされても大丈夫。
if (other != null) {
other.Dispose();
other = null;
}
if (refwe != null) {
// サブスクライブしたイベントを解除
// More Effective C# 23
// イベントハンドラの定義宣言は省略...
refwe.Event -= refwe_Event;
refwe = null;
}
}
// 直接このクラスが管理しているアンマネージリソースの解放処理をここに書く。
// 仮に other がアンマネージリソースを保持していることを知っていても、
// ここで Dispose を呼び出してはいけない。
// ここはファイナライザからの呼び出しでも実行される場所で、ファイナライザからの
// 呼び出しである場合、other はすでにファイナライズされているかもしれない。
// .Net の GC は参照カウントでなくルート探索で動作するので、自分が参照して
// いるからといって、そのオブジェクトがファイナライズされていない保証はない。
// したがって、ここでは他のオブジェクトを参照してはいけない。
// http://msdn.microsoft.com/ja-jp/library/system.idisposable.dispose.aspx
// もし、IntPtr.Zero も有効な値なのであれば、
// 別のフラグを用意して、開放したかどうかを管理する。
if (unmanaged != IntPtr.Zero) {
UnmanagedResource.Free(unmanaged);
unmanaged = IntPtr.Zero;
}
// 単なる null 代入でいいものはここに書く。
AnyEvent = null;
// ObjectDisposedException を実装したければ、ここでフラグ立てるとかする。
// もし、このクラスが継承クラスなら、
// base.Dispose(disposing);
}
}
2012/05/18
IDispose 実装のパターン
忘れやすいので書き残しておく。