3

TransactionScopeとLinqtoSqlをペアにするクラスを作成しましたDataContext

TransactionScopeDispose()と同じメソッドを実装しComplete()、DataContextを公開します。

これは、DataContextが再利用されないようにすることを目的としています。これらは、単一のトランザクションとペアになって、一緒に破棄されます。

クラスにFinalizeメソッドを含める必要がありますか?まだ呼び出されていない場合は、Disposeを呼び出しますか?それとも、管理されていないリソースを参照するIDisposableの場合のみですか?

4

3 に答える 3

3

ファイナライザーは、管理されていないリソースをクリーンアップすることのみを目的としています。ファイナライザー内の依存オブジェクトの破棄を呼び出すことには意味がありません。これらのオブジェクトが重要なリソースを管理している場合、それらのオブジェクト自体がファイナライザーを持っているためです。

.NET 2.0以降では、ファイナライザーを実装する理由はさらに少なく、SafeHandleクラスを含む.NETはありません。

ただし、ファイナライザーをまだ実装していることが時々ある理由の1つは、開発者がDisposeを呼び出すのを忘れたかどうかを調べることです。このクラスにファイナライザーをデバッグビルドにのみ実装させ、デバッグウィンドウに書き込みます。

于 2012-08-15T08:34:10.130 に答える
3

いいえ、使い捨てクラスをラップしているという理由だけで、使い捨てクラスにファイナライザーを実装しないでください。

Disposeクラスとファイナライザーのクリーンアップシナリオが3つあると考えてください。

  1. Dispose()と呼ばれます。
  2. ファイナライザーは、アプリケーションのシャットダウン時に呼び出されます。
  3. オブジェクトは収集される予定でしたが、ファイナライザーは抑制されていませんでした(ほとんどの場合、への呼び出しからDispose()ですが、クリーンアップする必要がない状態になった場合は、常にファイナライザーを抑制してください。 、および必要な状態になっている場合は再登録します(たとえば、メソッドのOpen()/Close()ペアがある場合)。

ここで、管理されていないリソースを直接管理している場合(たとえば、を介したハンドルIntPtr)、これらの3つのシナリオでは、2つのクリーンアップ方法のいずれかが直接呼び出され、クリーンアップが必要な3つのシナリオと直接一致します。

わかった。したがって、「外部」クラスにファイナライザーが正しく実装されている使い捨てラッピングを考えてみましょう。

~MyClass()
{
  // This space deliberately left blank.
}

ファイナライザーは、処理するための管理されていないクリーンアップがないため、何もしません。唯一の効果は、このnullファイナライザーが抑制されていない場合、ガベージコレクション時にファイナライザーキューに入れられ、フィールドを介してのみ到達可能なものがすべて有効になり、最終的にはファイナライズに昇格することです。スレッドはこのnopメソッドを呼び出し、ファイナライズ済みとしてマークし、ガベージコレクションの対象になります。ただし、昇格されたため、Gen0の場合はGen1になり、Gen1の場合はGen2になります。

ファイナライズする必要があった実際のオブジェクトもプロモートされ、収集だけでなくファイナライズも少し長く待つ必要があります。何があっても、最終的には第2世代になります。

さて、それは十分に悪いことです。たとえば、ファイナライズ可能なクラスを保持するフィールドで何かを実行するコードをファイナライザーに実際に配置したとします。

待って。何しようか?ファイナライザーを直接呼び出すことはできないため、破棄します。ああ、待ってください。このクラスのファイナライザーの動作Dispose()とファイナライザーの動作は、安全であるほど十分に近いと確信していますか?ファイナライザーではなくdisposeメソッドで処理しようとする弱参照を介して、一部のリソースを保持していないことをどのようにして知ることができますか?そのクラスの作成者は、ファイナライザー内から弱参照を処理することの危険性についてすべて知っていますが、Dispose()他の誰かのファイナライザーメソッドの一部ではなく、メソッドを作成していると考えていました。

次に、アプリケーションのシャットダウン中に外部ファイナライザーが呼び出された場合はどうなりますか。内側のファイナライザーがまだ呼び出されていないことをどうやって知っていますか?クラスの作成者がDispose()メソッドを書いているとき、彼らは「わかりました。ファイナライザーがすでに実行された後に呼び出された場合を処理することを確認しましょう。このオブジェクトに残されているのは、それを持っていることだけです。メモリが解放されましたか?」あまり。それは彼らが繰り返しの呼び出しから守っていたのかもしれませんDispose()このシナリオからも保護するような方法で、しかしあなたは本当にそれに賭けることはできません(特に彼らがファイナライザーでそれを助けないので、彼らはこれまでに呼び出された最後のメソッドであり、他の種類のクリーンアップのようなそのようにフラグを立てるために再び使用されないフィールドをヌルにすることは無意味です)。参照カウントされたリソースの参照カウントを削除したり、処理するのが仕事であるアンマネージコードのコントラクトに違反したりするようなことになる可能性があります。

それで。このようなクラスのファイナライザーの最良のシナリオは、ガベージコレクションの効率を損なうことです。最悪の場合、支援しようとした完全に優れたクリーンアップコードを妨げるバグがあります。

Dispose(bool disposing)保護されたメソッドがある場合に、MSがプロモートするために使用する(そして一部のクラスにはまだ存在する)パターンの背後にあるロジックにも注意してください。さて、このパターンについて言うべき悪いことがたくさんありますが、それを見ると、クリーンアップするものとファイナライザーでクリーンアップするものが同じではないという事実に対処するように設計されていますDispose()-このパターンは、オブジェクトの直接保持されている非管理対象リソースが両方の場合にクリーンアップされ(質問のシナリオでは、そのようなリソースはありません)、内部保持されてIDisposableいるオブジェクトなどの管理対象リソースは、からのDispose()クリーンアップされることを意味します。ファイナライザーからではありません。

管理されていないリソース、オブジェクトなど、クリーンアップする必要があるものIDisposable.Dispose()がある場合は実装します。IDisposable

クリーンアップする必要のある管理されていないリソースが直接ある場合にのみ、ファイナライザーを作成し、そこで行う唯一のことをクリーンアップします。

ボーナスポイントについては、両方のクラスに同時に参加することは避けてください。すべてのアンマネージリソースを、そのアンマネージクラスのみを処理する使い捨てのファイナライズ可能なクラスにラップします。その機能を他のリソースと組み合わせる必要がある場合は、そのようなクラス。そうすることで、クリーンアップがより明確になり、単純になり、バグが発生しにくくなり、GCの効率へのダメージが少なくなります(あるオブジェクトのファイナライズが別のオブジェクトのファイナライズを遅らせるリスクがなくなります)。

于 2012-08-15T09:56:21.860 に答える
1

これに対する簡単な答えはありません-それは議論の余地があります。

何?

議論は、完全なDisposableパターンでファイナライザーを使用するかどうかです。コードはトランザクションとデータベースコンテキストを使用します-それらの人は(通常)管理されていないリソース(カーネルトランザクションオブジェクトやTCP / IP接続など)を使用します

なんで?

クリーンアップする必要のある管理されていないリソースを使用する場合は、を実装する必要がありますIDisposable。次に、クライアントコードは、クラスへの呼び出しを推奨されるusing(IDisposable myClass = new MyClass(){...}構成にラップできます。問題は、開発者が明示的または暗黙的に呼び出さない場合IDisposable.Dispose()、リソースが自動的に解放されないことです。オブジェクトmyClassがGCによって収集された場合でも。これは、GCがDispose収集中に呼び出すことはなく、終了キューの責任であるためです。

したがって、ガベージコレクションから独立した、最終的にGCfinilisationスレッドによって呼び出されるfiniliserを定義できます。

意見

一部の人々は、すべての使い捨てコードを入れて、using (){}完成を忘れることを確認する必要があると主張します。結局のところ、そのようなリソースをできるだけ早くリリースする必要があり、ファイナライズプロセス全体は多くの開発者にとってやや曖昧です。

対照的に、コードを誰が使用するかわからないという理由だけで、finilisatorを明示的に実装することを好みます。したがって、誰かがDisposeそれを必要とするクラスでを呼び出すのを忘れた場合、リソースは最終的に解放されます。

結論

個人的には、を実装するクラスでfinilisatorを実装することをお勧めしますIDisposable

于 2012-08-15T08:34:55.480 に答える