IDisposable の正しい実装
.NET でオブジェクト リソースを解放するために使用されるインターフェイスは IDisposable ですが、このインターフェイスの実装にはさらに、Finalize と Close という 2 つの関数があります。
MSDN では、次のパターンに従って IDisposable インターフェイスを実装することをお勧めします。
1 パブリック クラス Foo: IDisposable
2 {
3 public void Dispose()
4 {
5 破棄(true);
6 GC.SuppressFinalize(this);
7}
8
9 protected virtual void Dispose(bool 破棄)
10 {
11 if (!m_disused)
12 {
13 if (処分)
14 {
15 // 管理リソースを解放する
16}
17
18 // アンマネージリソースを解放する
19
20 m_dispose = true;
21 }
22 }
23
24 ~ふー()
25 {
26 破棄(false);
27}
28
29 プライベートブール m_disused;
30}
31
32
実際には、.NET オブジェクト内のリソースを解放する関数は、Dispose と Finalize の 2 つです。 Finalize の目的はアンマネージド リソースを解放することですが、Dispose はマネージドおよびアンマネージドを含むすべてのリソースを解放するために使用されます。
このモードでは、 void Dispose(bool disposing) 関数は、処分パラメータを使用して、現在 Dispose() によって呼び出されているかどうかを区別します。 Dispose() によって呼び出された場合は、マネージド リソースとアンマネージド リソースの両方を同時に解放する必要があります。 ~Foo() (つまり、C# の Finalize()) によって呼び出された場合は、アンマネージ リソースを解放するだけで済みます。
これは、Dispose() 関数が他のコードによって明示的に呼び出され、リソースの解放が必要となるのに対し、Finalize は GC によって呼び出されるからです。 Foo によって参照される他の管理オブジェクトは、GC の呼び出し時に破棄する必要がない場合があります。また、破棄された場合でも、GC によって呼び出されます。したがって、Finalize で解放する必要があるのは、管理されていないリソースのみです。一方、Dispose()でマネージリソースとアンマネージリソースが解放されているため、オブジェクトがGCでリサイクルされる際に再度Finalizeを呼び出す必要がないため、重複を避けるためにDispose()内でGC.SuppressFinalize(this)を呼び出します。仕上げます。
ただし、Finalize と Dispose を繰り返し呼び出しても問題はありません。変数 m_disposed の存在により、リソースの解放は 1 回だけであり、重複した呼び出しは無視されます。
したがって、上記のパターンでは次のことが保証されます。
1. Finalize は
管理対象リソースと管理対象外リソースのみを解放します。
3. Finalize と Dispose は同じリソース解放戦略を共有するため、それらの間に競合はありません
。
。
C# では、このパターンを明示的に実装する必要があります。C# の ~Foo() 関数は Finalize() を表します。 C++/CLI では、このモードは自動的に実装されますが、C++ クラス デストラクターは異なります。
C++ のセマンティクスによれば、デストラクターはスコープ外に出るか削除するときに呼び出されます。マネージド C++ (つまり、.NET 1.1 のマネージド C++) では、デストラクターは、ガベージ コレクション中に GC によって呼び出される CLR の Finalize() メソッドと同等であるため、呼び出しのタイミングは不明瞭です。 .NET 2.0 の C++/CLI では、デストラクターのセマンティクスが Dispose() メソッドと同等になるように変更されました。これは次の 2 つのことを意味します。
1. C++/CLI のすべての CLR クラスは IDisposable インターフェイスを実装しているため、using キーワードを使用して C# でこのクラスのインスタンスにアクセスできます。
2. デストラクターは Finalize() と同等ではなくなりました。
最初の点については、これは良いことです。意味的には Dispose() は C++ デストラクターに近いと思います。 2 番目の点については、Microsoft は以下に示すように、「!」関数を導入して拡張を作成しました。
1 public ref class Foo
2 {
3 公開:
4Foo();
5 ~Foo() // デストラクター
6 !Foo() // ファイナライザ
7};
8
「!」関数 (実際には何と呼ぶのかわかりません) は、マネージド C++ の元の Finalize() を置き換えるもので、GC によって呼び出されます。 MSDN では、コードの重複を減らすために、次のようなコードを記述することをお勧めします。
1 ~Foo()
2 {
3 //管理リソースを解放する
4 this->!Foo();
5}
6
7 !フー()
8 {
9 //アンマネージドリソースを解放する
10}
11
上記のクラスの場合、C++/CLI によって生成された対応する C# コードは実際には次のようになります。
1 パブリック クラス Foo
2 {
3 プライベート void !Foo()
4 {
5 // アンマネージリソースを解放する
6}
7
8 プライベート void ~Foo()
9 {
10 // 管理リソースを解放する
11 !Foo();
12}
13
14 publicFoo()
15 {
16}
17
18 パブリック void Dispose()
19 {
20 破棄(true);
21 GC.SuppressFinalize(this);
22 }
23
24 protected virtual void Dispose(bool disposing)
25 {
26 if (処分)
27 {
28 ~Foo();
29 }
他30
31 {
32 トライ
33 {
34 !Foo();
35}
36 ついに
37 {
38 ベース.ファイナライズ();
39 }
40}
41 }
42
43 protected void Finalize()
44 {
45 破棄(false);
46 }
47 }
48
~Foo() と !Foo() は繰り返し呼び出されないため (少なくとも MS はそう考えています)、このコードには前の m_dissolved と同じ変数はありませんが、基本構造は同じです。
さらに、実際には Dispose と Finalize である ~Foo() と !Foo() ではないことがわかりますが、C++/CLI コンパイラは 2 つの Dispose 関数と Finalize 関数を生成し、適切なタイミングでそれらを呼び出します。 C++/CLI は実際に多くの作業を行ってきましたが、唯一の問題は、ユーザーが ~Foo() で !Foo() を呼び出すことに依存していることです。
リソースの解放に関して最後に言及するのは、Close 機能です。 MSDN によると、この関数は、ファイルなどの特定のオブジェクトに対して Close() を呼び出すことに慣れているため、ユーザーがより快適に感じられるようにするために提供されています。
ただし、結局のところ、これら 2 つの関数は同じことを行うため、MSDN が推奨するコードは次のとおりです。
1 パブリック void Close()
2 {
3 破棄(();
4}
5
6
ここでは、パラメータなしの Dispose 関数が直接呼び出され、Dispose と同じセマンティクスが取得されます。これで完璧なように見えますが、一方で、Dispose と Close が同時に提供されると、ユーザーに混乱が生じます。コードの詳細を見ないと、これら 2 つの関数の違いを知るのは困難です。したがって、.NET コード設計仕様では、ユーザーが実際に使用できるのはこれら 2 つの関数のうち 1 つだけであると規定されています。したがって、推奨されるパターンは次のとおりです。
1 パブリック クラス Foo: IDisposable
2 {
3 public void Close()
4 {
5 破棄();
6}
7
8 void IDisposable.Dispose()
9 {
10 破棄(true);
11 GC.SuppressFinalize(this);
12}
13
14 protected virtual void Dispose(bool disposing)
15 {
16 // 以前と同じ
17}
18}
19
ここでは、インターフェイスのいわゆる明示的な実装、 void IDisposable.Dispose() が使用されています。この明示的な実装にはインターフェイスを通じてのみアクセスでき、実装クラスを通じてはアクセスできません。したがって:
1 Foo foo = new Foo();
2
3 foo.Dispose(); // エラー
4 (foo as IDisposable).Dispose(); // 正しい
5
これで両方が解決されます。 Close を使用したい人は、foo.Close() を直接使用できますが、Dispose() は表示されません。 Dispose を好む人は、型を IDisposable に変換して呼び出すか、using ステートメントを使用できます。どちらにとっても大きな喜びです!
http://www.cnblogs.com/xlshcn/archive/2007/01/16/idisposable.html