5

私は自分の理解IDisposableが正しいことを確認しようとしていますが、まだよくわからないことがあります。

IDisposable2つの目的を果たしているようです。

  1. 管理対象オブジェクトをオンデマンドで「シャットダウン」するための規則を提供するため。
  2. 管理対象オブジェクトが保持する「管理対象外のリソース」を解放するための規則を提供するため。

私の混乱は、どのシナリオで「管理されていないリソース」が機能しているのかを特定することから来ています。

Microsoftが提供するIDisposable実装(管理)クラス(たとえば、データベースまたはソケット関連)を使用しているとします。

  1. 上記の1IDisposableつだけで実装されているのか1&2で実装されているのかをどうやって知ることができますか?
  2. 内部に保持されているかどうかわからない管理されていないリソースが解放されるようにする責任はありますか?instanceOfMsSuppliedClass.Dispose()を呼び出す独自のクラスにファイナライザー(それは正しいメカニズムでしょうか?)を追加する必要がありますか?
4

8 に答える 8

10
  1. 上記の1または1&2にIDisposableを実装しているかどうかをどのように知ることができますか?

あなたの最初の質問に対する答えは「あなたは知る必要はないはずです」です。サードパーティのコードを使用している場合は、ある程度の慈悲があります。Disposeを呼び出すと、サードパーティのコードが適切に破棄されていることを信頼する必要があります。よくわからない場合やバグがあると思われる場合は、いつでもReflector()を使用して分解し(可能な場合)、何が行われているのかを確認できます。

  1. 内部に保持されているかどうかにかかわらず、管理されていないリソースが解放されるようにする責任はありますか?instanceOfMsSuppliedClass.Dispose()を呼び出す自分のクラスにファイナライザー(それは正しいメカニズムでしょうか?)を追加する必要がありますか?

.Net 2.0以降を使用している場合は、クラスにファイナライザーを実装する必要はほとんどありません。ファイナライザーはクラスにオーバーヘッドを追加し、通常、Disposeを実装するだけで必要となる機能を提供しません。適切な廃棄の概要については、この記事にアクセスすることを強くお勧めします。instanceofMSSuppliedClass.Dispose()あなたの場合、あなたはあなた自身のDispose()メソッド を呼び出したいと思うでしょう。

最終的に、オブジェクトに対してDispose()を呼び出すことは、リソースが完了したことをGCに明示的に通知し、ユーザーがリソースをすぐにクリーンアップできるようにし、他のプログラマーに通知することでコードを間接的に文書化できるため、良い習慣です。オブジェクトはその時点でリソースを使用して実行されます。ただし、明示的に呼び出すのを忘れた場合でも、オブジェクトがルート解除されたときに最終的に発生します(.Netは結局のところ管理されたプラットフォームです)。ファイナライザーは、オブジェクトに暗黙的なクリーンアップが必要な管理されていないリソースがある場合にのみ実装する必要があります(つまり、コンシューマーがクリーンアップを忘れる可能性があり、これが問題になる可能性があります)。

于 2009-06-18T15:22:36.017 に答える
5

IDisposableを実装するオブジェクトでは常にDisposeを呼び出す必要があります(ASP.NET MVCのHtmlHelper.BeginFormのように、これが便利な規則であると具体的に指示されていない限り)。これを簡単にするために、「using」ステートメントを使用できます。クラス内のIDisposableの参照をメンバーフィールドとして使用する場合は、Disposable Patternを使用してIDisposableを実装し、それらのメンバーをクリーンアップする必要があります。FxCopのような静的分析ツールを実行すると、同じことがわかります。

インターフェイスを二度と推測しようとしてはいけません。今日、そのクラスはアンマネージリソースを使用しない可能性がありますが、次のバージョンはどうでしょうか。

于 2009-06-18T15:21:49.653 に答える
1

「管理されていないリソース」という用語は、誤称のようなものです。基本的な概念は、ペアのアクションの概念です。1つのアクションを実行すると、クリーンアップアクションを実行する必要が生じます。ファイルを開くと、ファイルを閉じる必要があります。モデムにダイヤルすると、電話を切る必要があります。システムは、クリーンアップ操作の実行の失敗に耐えることができますが、結果は深刻な場合があります。

オブジェクトが「管理されていないリソースを保持している」と言われる場合、実際の意味は、オブジェクトが他のエンティティで必要なクリーンアップ操作を実行するために必要な情報と推進力を持っていることであり、情報と推進力がどこにでも存在すると信じる特別な理由はありません。そうしないと。そのような情報と推進力を持つ唯一のオブジェクトが完全に放棄された場合、必要なクリーンアップ操作は実行されません。.Disposeの目的は、オブジェクトに必要なクリーンアップを強制的に実行させて、オブジェクトを安全に破棄できるようにすることです。

最初にDisposeを呼び出さずにオブジェクトを破棄するコードに対する保護を提供するために、システムはクラスが「ファイナライズ」に登録できるようにします。登録されたクラスのオブジェクトが放棄された場合、システムは、オブジェクトが完全に放棄される前に、他のエンティティに対してクリーンアップ操作を実行する機会をオブジェクトに与えます。ただし、オブジェクトが破棄されたことをシステムがどれだけ迅速に認識するかについての保証はありません。また、さまざまな状況により、オブジェクトがクリーンアップされる機会が提供されない場合があります。「管理対象リソース」という用語は、破棄される前に何らかのクリーンアップを実行する必要があるが、誰かがDisposeを呼び出さなかった場合に自動的に登録され、そのようなクリーンアップを実行しようとするオブジェクトを指すために使用されることがあります。

于 2011-02-20T21:30:21.263 に答える
1

オブジェクトの内容については、あなたは責任を負いません。Dispose()は透過的であり、解放する必要があるものを解放する必要があります。その後、あなたはそれに対して責任を負いません。

アンマネージリソースは、(マネージ)C ++で作成するようなリソースであり、「gcnew」ステートメントではなく、ポインターと「new」ステートメントを介してメモリを割り当てます。C ++でクラスを作成する場合、このメモリはネイティブメモリまたはアンマネージであり、ガベージコレクタはそれを考慮しないため、このメモリを削除する必要があります。また、マーシャルの割り当てを介してこのアンマネージメモリを作成することもできます。これは、安全でないコードだと思います。

Managed C ++を使用する場合、IDisposableクラスを手動で実装する必要もありません。デコンストラクターを作成すると、Dispose()関数にコンパイルされます。

于 2009-06-18T15:23:08.667 に答える
1

問題のクラスがMicrosoft提供(データベースなど)の場合、(IDisposableからの)Disposeの処理はすでに処理されている可能性が高いため、呼び出すのはユーザーの責任です。たとえば、データベースを使用する標準的な方法は次のようになります。

//...
using (IDataReader dataRead = new DataReaderObject())
{
   //Call database
}

これは基本的に次のように書くのと同じです。

IDataReader dataRead = null;
try
{
    dataRead = new DataReaderObject()
    //Call database
}
finally
{
    if(dataRead != null)
    {
        dataRead.Dispose();
    }
}

私が理解していることから、IDisposableから継承するオブジェクトには前者を使用することをお勧めします。これにより、リソースが適切に解放されるようになります。

IDisposableを自分で使用する場合、実装はあなた次第です。継承したら、手動で作成したDB接続を破棄するために必要なコードがメソッドに含まれていることを確認する必要があります。残っている可能性のあるリソースを解放したり、オブジェクトの破棄を防止したり、大きなリソースプール(画像など)をクリーンアップしたりする必要があります。 )。これには、管理されていないリソースも含まれます。たとえば、「安全でない」ブロック内にマークされたコードは、本質的に管理されていないコードであり、メモリを直接操作できるため、確実にクリーンアップする必要があります。

于 2009-06-18T15:35:37.830 に答える
0

はい、あなたはDisposeメソッドを呼び出す責任があります-またはより良いuseusingステートメント。オブジェクトが実装されている場合はIDisposable、何があっても常にそれを破棄する必要があります。

using (var myObj = new Whatever())
{
   // ..
}

と類似しています

{
  var myObj;
  try
  {
     myObj = new Whatever();
     // ..
  } 
  finally
  {
    if (myObj != null)
    {
      ((IDisposable)myObj).Dispose();
    }
  }
} // object scope ends here

編集:Talljoeのおかげでtry / finalを追加しました-うわー、それを正しくするのは複雑です:)

EDIT2:私はあなたが2番目の変種を使うべきだと言っているのではありません。「使用する」ことは、非常に厄介で正しく理解するのが難しい一連のコードにとって、優れた構文糖衣であることを示したかっただけです。

于 2009-06-18T15:21:01.043 に答える
0

なぜそれがあなたにとって重要なのですか?

可能であれば、使い捨てオブジェクトのスコープを使用してラップします。この呼び出しは、使用の最後に破棄します。そうでない場合は、オブジェクトが不要になったときにdisposeを明示的に呼び出します。

理由1または理由2のどちらであるかは必要ありません。

于 2009-06-18T15:24:35.467 に答える
-1

ここで欠落しているのはファイナライザーです。私の習慣は、IDisposableを実装する場合、クライアントが実装しない場合に備えてDispose()を呼び出すファイナライザーも持っていることです。はい、オーバーヘッドが追加されますが、Dispose()がGC.SuppressFinalize(this)呼び出しよりも呼び出された場合、それは排除されます。

于 2009-06-19T13:12:24.603 に答える