SomeDisposableObject
以下を実装するクラスがある場合IDisposable
:
class SomeDisposableObject : IDisposable
{
public void Dispose()
{
// Do some important disposal work.
}
}
また、 という別のクラスがありAContainer
、そのクラスには のインスタンスがSomeDisposableObject
パブリック プロパティとして含まれています。
class AContainer
{
SomeDisposableObject m_someObject = new SomeDisposableObject();
public SomeDisposableObject SomeObject
{
get { return m_someObject; }
set { m_someObject = value; }
}
}
AContainer
すると、FxCop は、これも作成されたと主張しますIDisposable
。
それは問題ありませんが、別のクラスがインスタンスへの参照を保持している可能性があるため、m_someObject.Dispose()
から安全に呼び出す方法がわかりません。AContainer.Dispose()
m_someObject
このシナリオを回避する最善の方法は何ですか?
(他のコードはAContainer.SomeObject
常に非 null 値を持つことに依存していると仮定すると、インスタンスの作成を単に外部に移動することはAContainer
オプションではありません)
編集: コメントの中には問題を見逃している人もいると思うので、いくつか例を挙げて説明します。m_someObject.Dispose ()Dispose()
を呼び出すメソッドを実装するだけではAContainer
、次のような状況になります。
// Example One
AContainer container1 = new AContainer();
SomeDisposableObject obj1 = container1.SomeObject;
container1.Dispose();
obj1.DoSomething(); // BAD because obj1 has been disposed by container1.
// Example Two
AContainer container2 = new AContainer();
SomeObject obj2 = new SomeObject();
container2.SomeObject = obj2; // BAD because the previous value of SomeObject not disposed.
container2.Dispose();
obj2.DoSomething(); // BAD because obj2 has been disposed by container2, which doesn't really "own" it anyway.
それは役に立ちますか?
ベストアンサー1
答えは一つではなく、シナリオによって異なります。重要な点は、プロパティによって表される使い捨てリソースの所有権です。ジョン・スキートは指摘する。
.NET Framework の例を調べると役立つ場合があります。ここでは、動作が異なる 3 つの例を示します。
コンテナは常に破棄するSystem.IO.StreamReader は、破棄可能なプロパティ BaseStream を公開します。これは基になるストリームを所有するものとみなされ、StreamReader を破棄すると、常に基になるストリームが破棄されます。
コンテナは決して廃棄しないSystem.DirectoryServices.DirectoryEntry は Parent プロパティを公開します。親を所有しているとはみなされないため、DirectoryEntry を破棄しても親は破棄されません。
この場合、Parent プロパティが逆参照されるたびに新しい DirectoryEntry インスタンスが返され、呼び出し元はそれを破棄することが想定されます。おそらくこれはプロパティのガイドラインに違反しており、代わりに GetParent() メソッドを使用する必要があると考えられます。
コンテナは時々廃棄するSystem.Data.SqlClient.SqlDataReader は破棄可能な Connection プロパティを公開しますが、呼び出し元は SqlCommand.ExecuteReader の CommandBehavior 引数を使用して、リーダーが基になる接続を所有しているかどうか (したがって破棄するかどうか) を決定します。
もう 1 つの興味深い例は、読み取り/書き込み可能な破棄可能なプロパティ SearchRoot を持つ System.DirectoryServices.DirectorySearcher です。このプロパティが外部から設定された場合、基になるリソースは所有されていないとみなされるため、コンテナーによって破棄されません。外部から設定されていない場合は、内部で参照が生成され、破棄されるようにフラグが設定されます。これは、Lutz Reflector で確認できます。
コンテナがリソースを所有しているかどうかを決定し、その動作を正確に文書化する必要があります。
リソースを所有していることを決定し、プロパティが読み取り/書き込み可能である場合、セッターが置き換える参照をすべて破棄するようにする必要があります。例:
public SomeDisposableObject SomeObject
{
get { return m_someObject; }
set
{
if ((m_someObject != null) &&
(!object.ReferenceEquals(m_someObject, value))
{
m_someObject.Dispose();
}
m_someObject = value;
}
}
private SomeDisposableObject m_someObject;
アップデート: GrahamS はコメントで、破棄する前にセッターで m_someObject != 値をテストする方がよいと正しく指摘しています。これを考慮して上記の例を更新しました (明示的に != ではなく ReferenceEquals を使用)。ただし、多くの実際のシナリオでは、セッターの存在は、オブジェクトがコンテナーによって所有されていないことを意味する可能性があり、したがって破棄されません。