オブジェクトの有効期間とそのリソースのクリーンアップが密接に関連している C++ とは異なり、.NET ではそれらはほとんど切り離されています。.NET のオブジェクトは、システムがそのオブジェクトについて「認識」している限り存続します。または、それらへの参照が、システムが認識している他のオブジェクトに保持されます。オブジェクトへの最後の参照が上書きされると、オブジェクトは存在しなくなります。システムは特定のオブジェクトへの「非表示の」参照を保持していますが (たとえば、登録されているすべてのオブジェクトのリストがあります)Finalize
メソッド)、多くの場合、特定のオブジェクトが存在したことを示す唯一の証拠は、そのオブジェクトへのユーザー コード参照です。たとえば、GC が認識しているものによって使用されていない 1024 バイトの範囲のメモリがある場合、GC は、その領域が 16 個の 64 バイト オブジェクト、12 個の 84 バイト オブジェクト、および16 バイト オブジェクト、またはその他のオブジェクトの組み合わせ。
このアプローチは、メモリの管理に非常に適しています。これに関する 1 つの問題は、追って通知があるまで、オブジェクトが他のエンティティに何かを行うように要求する場合 (ファイルへの排他的アクセスを許可するなど) に発生します。ファイルへの排他的アクセスを要求するオブジェクトが、そのようなアクセスが不要になったことを誰にも知らせずに単に存在しなくなった場合、そのファイルは不必要に他の人からアクセスできなくなります。このIDisposable
インターフェースは、この問題をいくらか解決します。メソッドが呼び出されるオブジェクトDispose
は、そのようなサービスを必要としないことを、通知があるまで、自分に代わって何かを行うように要求したすべてのエンティティに通知する必要があります。
適切に使用するための鍵IDisposable
は、クリーンアップを必要とするすべてのオブジェクトが常に 1 つの「所有者」を持つようにすることです。using
その所有者は、またはtry
/ブロックを介して保護されるローカル変数、finally
または を実装するオブジェクトのフィールドである必要があり、独自のメソッドが呼び出されたときにフィールドにIDisposable
なります。を所有するローカル変数を持つメソッドが、呼び出しを行わずにその変数を返す場合、所有権はメソッドの呼び出し元に転送されます。C# には、メソッド内で作成されたオブジェクトがメソッドから返された後に不要になる場合を除いて、所有権を認識するための言語構造がないことに注意してください (Dispose
Dispose
IDisposable
Dispose
using
ブロックはそのケースを適切に処理します)。それ以外の場合、プログラマはオブジェクトの所有権を手動で追跡する必要があります。そうしなくてもコンパイル エラーは発生しませんが、多くの場合、プログラムが動作しなくなったり、サービスが不要になったという通知を外部のエンティティが不必要に待ったりする原因になります。
の場合はreturn new MyIDisposableObject();
、上記のパターンのいずれにも完全には適合しませんが、try/finally によって保護されている場合は次のようになるため、許容されます。
bool ok = false;
MyIDisposableObject ret = null;
try
{
ret = new MyIDisposableObject();
ok = true;
return ret;
}
finally
{
if (!ok && ret != null)
ret.Dispose();
}
ステートメントが実行される唯一の方法は、store toと次の return のret.Dispose()
間で例外が発生した場合です。ret
コードがreturn new MyIDisposableObject();
のように記述されている場合、そこで例外が発生する可能性はありません。
ただし、別の関数呼び出しを追加するため、コードは異なります。ただし、そのパターンはMyMethod
、渡されたオブジェクトをカプセル化するオブジェクトを返すことが約束されている場合、またはDispose
例外のためにカプセル化できない場合にのみ安全です。MyMethod
通常はそうではないため、 が渡された参照をカプセル化するオブジェクトを返すかどうかに応じて、正しいパターンは次のいずれかになります。
using (var myObject = new MyDisposableObject())
return MyMethod(myObject);
がによって返されるオブジェクトにカプセル化されMyDisposableObject
ず、MyMethod
返された後はそれ以上使用されない場合、またはそうでない場合
MyIDisposableObject innerObject = new MyDisposableobject;
try
{
var ret = MyMethod(innerObject);
innerObject = null; // Note that `ret` still has an encapsulated reference to it
return ret;
}
finally
{
if (innerObject != null) // Reference wasn't yet encapsulated in `ret`
innerObject.Dispose();
}
の呼び出しがMyMethod
成功した場合、innerObject
変数はクリアされますが、オブジェクトは外部エンティティにサービスを中止するよう通知するように指示されないことに注意してMyMethod
くださいinnerObject
。それらの外部エンティティのサービスが必要になります。への呼び出しMyMethod
で例外がスローされた場合、innerObject
変数はクリアされず、finally
ブロック コードは への唯一の参照を保持していることinnerObject
、その参照が消えようとしていることがわかり、他のコードは を使用しなくなりますinnerObject
。したがって、finally
ブロックは、innerObject
外部エンティティにサービスが不要になったことをすぐに通知するように依頼する必要があります。、そうしない場合は、他に何もしません。