Disposeパターンについて調べたので結果をまとめてみようと思います。
Disposeパターン
Disposeパターンですが、クラスの特徴が
- アンマネージリソースを使用する
- 派生クラスで継承される予定がある
場合、クラスにIDisposeメソッドを付けてリソース解放を明示的に行う必要がある場合に使用します。特に、基底クラスのDisposeメソッド呼び出しにり、派生クラス側の資源解放を同時に行いたい場合これを適用します。
最近は VisualStudoo2019 を使用しているとサジェストに「破棄パターンを使ってインターフェースを実装します」というメッセージが表示されるときは以下のパターンが自動的に実装されます。ただし説明はないため以下のコード内のコメントの箇所を理解していることが大切です。
コード例
Disposeパターンの実装例は以下の通りです。
// 基底クラスの実装 public class Base : IDisposable { // Disposeされたかどうかを表すフラグ // true : Dispose済み // false : Disposeまだ private bool disposed; // IDisposableの実装 public Dispose() { this.Dispose(true); // Dispoaseが呼ばれた場合デストラクタを呼ばない指示 // → 2重解放しないようにする GC.SuppressFinalize(this); } ~Base() => this.Dispoase(false); // デストラクタ経由の時はマネージリソースは解放しない // Disposeパターン用のメソッド // // disposing // true : Disposeメソッドからの呼び出し // false : デストラクタからの呼び出し protected virtual void Dispose(bool disposing) { if(this.disposed) { return; // 解放済みなので処理しない } if(disposing) { // マネージリソースの解放処理を記述 } // アンマネージリソースの解放処理を記述 // Dispose済みを記録 this.disposed = true; } }
派生クラスの実装
上記パターンを持つクラスを継承した場合、派生クラス側でも考慮することがあります。disposedの状態管理とDisposeメソッドのオーバーライドです。これを正しく行わないと自分のクラスだけリソースが解放されてしまったり、基底クラスだけリソースが解放されてしまう状態が発生します。
public class Derived : Base { // Baseクラスのメソッドをoverrideしている protected override void Dispose(bool disposing) { // 基底クラスのつくりと同じ ----> if(this.disposed) { return; // 解放済みなので処理しない } if(disposing) { // マネージリソースの解放処理を記述 } /// アンマネージリソースの解放処理を記述 // Dispose済みを記録 this.disposed = true; // <---- 基底クラスのつくりと同じ // ★★★忘れずに、基底クラスの Dispose を呼び出す【重要】 base.Dispose(disposing); } }
利用側のコード
上記コードの使用した場合のコード例となります。
public class AppMain { public void Main(string[] args) { // ☆使用例1☆ Base base_1 = new Derived(); base_1.Dispose(); // Derived.Dsipose(bool) → Base.Dispose(bool) の順に呼ばれる // ☆使用例2☆ using(Base base_2 = new Derived()) { // (省略) } // この時点で base_1 の Dipose と同じ動きになる // ☆使用例3☆ Derived derived = new Derived(); ((Base)derived).Dipose(); // これも Derived.Dsipose(bool) → Base.Dispose(bool) の順に呼ばれる // ☆使用例4☆ using(Derived base_2 = new Derived()) { // (省略) } // これも同じ } }
もしこのコードを他人に配るような場合、公開メソッドには以下のようにDipose済みだったら例外を投げる処理を実装をした方が幸せになれるかもしれません。
public void Foo() { if(this.disposed) { throw new InvaliedOperationException("Object is disposed."); } }
注意点
コード中のコメントの通りですが、Dipose(bool disposing)を各々のクラスで実装する必要があり、それぞれ手書きする必要があります。(面倒なので世の中テンプレートを配布されている方も居るのでそれらを使った方が確実です)
また、派生クラス側での注意点以下の通りです。
- IDisposableを継承しない
- デストラクタ(Finaliza)を実装しない
- base.Disposeメソッドの呼び出しを忘れない
- Dispose(bool)は protected virtual を忘れない
全ての派生クラスでIDisposableを継承せずに基底クラスのDispose経由で呼び出される事を意図しています。
参考資料
以下資料を参考に書きました。
読まないとモグリ、Effective C#

- 作者:ビル・ワグナー
- 発売日: 2011/02/16
- メディア: 大型本
MSDNの「Diposeパターン」
https://msdn.microsoft.com/ja-jp/library/b1yfkh5e.aspx
Dispose にまつわる余談 - ++C++飛行