10

~MyClass()C# で と書くと、これは基本的に に変換されることを私は知っていますoverride System.Object.Finalize()。したがって、デストラクタを記述するかどうかに関係なく、CLR のすべての型には(少なくとも)Finalize()メソッドが含まれます。System.Object

1] では、すべてのオブジェクトがデフォルトでファイナライザーを持っているということですか?

2] CLR がオブジェクトをファイナライズ キューに入れる必要があると判断する根拠は何ですか?

私はクラスを持っていたので、私はこれを求めていManagedResourceHolderます。このクラスは管理されていないリソースをまったく保持せず、メソッドも 必要ありませんでした。つまり、 finalizerがなかったため、呼び出しが必要ありませんでした。IDisposableGC.SuppressFinalize(this)IDisposable.Dispose()~ManagedResourceHolder()GC.SuppressFinalize(this)

3] 上記のシナリオのコンテキストでは、IDisposable を実装するときに常にファイナライザーを提供する必要がありますか? (アンマネージ リソースを保持しないクラスでも)

FxCop ルールCA1816により、これに関する違反が発生し、MSDN の CA フォーラムで質問したときに得た回答に混乱しました

ありがとう。

4

4 に答える 4

17

質問 1 と 2 : CLR は基本的に、ファイナライザーがオーバーライドされているかどうかをチェックします。そうでない場合は、ファイナライザーがないものとして扱います。

System.Object にファイナライザーを配置する利点は、コンパイラーがいつでも呼び出しを実行できることを認識していることbase.Finalize()です。これにより、バージョン管理の問題が回避されます。のない世界を考えてみましょうSystem.Object.Finalize():

  • System.Object (ファイナライズなし)
  • Acme.BaseClass (ファイナライズなし)
  • MyCompany.DerivedClass (ファイナライズ)

オブジェクトにメソッドがないFinalizeと、MyCompany.DerivedClass のファイナライザーは何も呼び出すことができません。Acme.BaseClass のバージョン 2 にファイナライザーが含まれている場合、問題が発生ます。MyCompany.DerivedClass を再コンパイルしない限り、DerivedClass のインスタンスは BaseClass.Finalize を呼び出さずにファイナライズされます。これは明らかに悪いことです。

System.Object.Finalizeで同じ状況考えてみましょう。コンパイラは、base.Finalize への呼び出しを自動的に DerivedClass.Finalize に挿入します。バージョン 1 では、System.Object の no-op 実装を呼び出すだけです。Acme.BaseClass のバージョン 2 が登場すると、base.Finalize(DerivedClass の再コンパイルなしで) への呼び出しは BaseClass.Finalize を呼び出します。

質問 3 : いいえ、IDisposable を実装するという理由だけでファイナライザーを用意する必要はありません。ファイナライザーは、他に何もクリーンアップしないアンマネージ リソース(つまり、直接参照しているリソース) に対してのみ使用する必要があります。たとえば、FileStreamメンバー変数を持つクラスがあるとします。IDisposable呼び出し元が覚えている場合は、できるだけ早くストリームを閉じることができるように実装したいと考えていますが、呼び出すことを覚えていないDispose()場合、ストリームはオブジェクトと同時にガベージ コレクションの対象になります。FileStream独自のファイナライザーでクリーンアップしようとするのではなく、適切なファイナライザー (またはファイナライザーなどを使用した他のものへの参照) があることを信頼してください。

.NET 2.0 の時点で、SafeHandleクラスを使用すると、独自のファイナライザーが必要になることはほとんどありません。

于 2008-12-11T08:56:03.537 に答える
3

1: オーバーライドされた場合にのみ (有用な意味で) カウントされます。

2: 1 で定義されているとおり、GC.SuppressFinalize が呼び出されていない (さらに再登録など)

3: 確かにそうではありません。実際、アンマネージ リソースを直接処理する場合を除き、ファイナライザーを使用するべきではありません。IDisposable であるという理由だけでファイナライザーを追加するべきではありませんが、ファイナライザーを持つものは一般的に IDisposable にする必要があります。

于 2008-12-11T09:00:26.180 に答える
0
  1. いいえ、そうではありません。オーバーライドFinalize()されたものだけが CLR によってカウントされます。
  2. 上で定義したように、ファイナライザーを使用します。
  3. いいえ、必ずしも必要ではありません。ちょうどいいパターンです。つまり、誰もあなたにそうするように強制しません。ただし、管理されていないリソースがある場合は、それを行うのは良いことです。誰かがそれを破棄するのを忘れると、管理されていないリソースがいつか解放されるからです。FxCop は厳密な規則を適用しません。気をつけないと、将来的に失敗につながる可能性のある良いパターンが強制されます。

更新: すべてのクラスは、独自のリソースを管理する責任があります。オブジェクト指向パラダイムの抽象化およびカプセル化機能により、クラスの消費者は、間接的にどのようなリソースを持っているかを気にするべきではありません。したがって、所有しているリソースを手動で解放するか (他のものをブラック ボックスとして見ると、所有しているものは直接所有しているものです)、GC に任せて解放する必要があります。管理されていないリソースの場合、GC に任せるオプションがないため、手動で解放する必要があります。この意味で、Jon が言及した SafeHandle は、管理されていないリソースの管理された抽象化です。したがって、貴重な管理対象リソース (非管理対象リソース自体のファイナライズを管理するブラック ボックス) として扱う必要があります。

于 2008-12-11T08:55:40.043 に答える
-2

1) はい (継承により)

2)クラスのインスタンスへの参照を保持しているものはありません(これにより、ファイナライズの対象になります)

3) はい (ユーザーが明示的に dispose を呼び出す必要がない限り、なぜ IDisposable を実装する必要があるのでしょうか? .net の接続クラスは内部で umanaged リソースを使用します。 GC のタイミングは不明であるため、その時点まで接続は開いたままになります)。

これが私の理解です。

私は間違っているかもしれません。その場合、専門家が私のために物事を修正します。

于 2008-12-11T09:01:29.970 に答える