ベストプラクティス:読み取り専用ICollectionを公開する方法
-
08-07-2019 - |
質問
クラスにICollection<T>
というfoos
があり、読み取り専用として公開したい(この質問)。インターフェイスがプロパティ.IsReadOnly
を定義していることがわかりますが、これは適切だと思います...私の質問は次のとおりです。
.Add()
などの実装されていないメソッドを試行する前に、ReadOnlyCollection<T>
を照会することを忘れないでください。理想的には、IList<T>
をfoo
として公開したいのですが、GetReadOnlyFooCollection
を実装していません。プロパティではなく、たとえばToList()
というメソッドを介して<=>を公開する必要がありますか?もしそうなら、これは<=>を期待する人を混乱させないでしょうか?
これはC#2.0であるため、<=>などの拡張メソッドは使用できません...
解決
クローンされたオブジェクトを使用して IEnumerableを返すことに決めたようです。
public IEnumerable<Foose> GetFooseList() {
foreach(var foos in Collection) {
yield return foos.Clone();
}
}
- FoosでCloneメソッドが必要です。
これにより、コレクションの変更は許可されません。 ReadonlyCollectionは<!> quot; leaky <!> quot;であることを忘れないでください。別の投稿のリンクに記載されているように、内部のオブジェクトを変更できるためです。
他のヒント
<!> quot; foos <!> quot;を作成できます。このようなReadOnlyCollection:
ReadOnlyCollection<T> readOnlyCollection = foos.ToList<T>().AsReadOnly();
その後、クラスのプロパティとして公開できます。
編集:
class FooContainer
{
private ICollection<Foo> foos;
public ReadOnlyCollection<Foo> ReadOnlyFoos { get { return foos.ToList<Foo>().AsReadOnly();} }
}
注:ReadOnlyFoosコレクションを取得すると、<!> quot; synchronized <!> quot;あなたのfoos ICollectionで。 参照したスレッド。
質問が書かれて以来、.NET 4.0はIReadOnlyCollection<T>
インターフェースを追加しました。宣言された戻り値の型としてそれを使用するのがおそらく良いでしょう。
ただし、返されるインスタンスのタイプの問題は未解決のままです。 1つのアプローチは、元のコレクションのすべてのアイテムを複製することです。もう1つは、常に読み取り専用のラッパーを返すことです。 3番目の方法は、List<T>
を実装する場合、元のコレクションを返すことです。各アプローチは特定のコンテキストでは最適なアプローチですが、他のアプローチでは理想的とは言えません(または恐ろしく恐ろしい)。残念ながら、Microsoftは質問に2つの非常に重要な質問をするための標準的な手段を提供していません。
-
あなたが今と同じアイテムを常にそして永遠に含むことを約束しますか?
-
コンテンツを変更することを想定していないコードに直接安全にさらされますか。
どのスタイルのラッピングが適切であるかは、クライアントコードが受け取ったものをどのように処理するかによって異なります。避けるべきいくつかのシナリオ:
-
クライアントが不変として認識するタイプのオブジェクトが提供されましたが、クライアントが不変として認識しないタイプを使用して、直接返されるのではなく、オブジェクトが複製されます。そのため、クライアントはコレクションを再度複製する必要があります。
-
クライアントが不変と認識するタイプのオブジェクトが提供されましたが、返される前に、クライアントがコレクションが不変かどうかを判断できないような方法でラップされているため、強制的に複製しました。
-
変更可能なタイプではないオブジェクトは、クライアントによって提供されます(読み取り専用インターフェイスにキャストされます)。その後、別のクライアントに直接公開され、クライアントは変更可能なタイプであると判断され、変更が続行されます。
-
可変コレクションへの参照が受信され、不変オブジェクトを必要とするクライアントに返される前に、読み取り専用ラッパーにカプセル化されます。コレクションを返すメソッドは、コレクションが不変であることを約束していたため、クライアントは独自の防御コピーを作成することを拒否しました。その後、クライアントは、コレクションが変更される可能性について十分な準備ができていません。
特に<!> quot; safe <!> quot;は特にありません。オブジェクトは、一部のクライアントから受信し、他のクライアントに公開する必要のあるコレクションを使用して実行できます。多くの場合、常にすべてを複製することは最も安全な措置ですが、複製する必要のないコレクションが何百または何千回も複製されるという状況が容易に発生する可能性があります。受け取った参照を返すことは、多くの場合最も効率的なアプローチですが、意味的にも危険です。
Microsoftが標準的な手段を追加して、コレクションに上記の質問をするか、さらに良いことに、現在の状態の不変のスナップショットを作成するように依頼することを望みます。不変のコレクションは、現在の状態の不変のスナップショットを非常に安価に返すことができ(それ自体を返すだけです)、一部の可変コレクションタイプでさえ、完全な列挙のコストをはるかに下回るコストで不変のスナップショットを返すことができます(たとえば、T[][]
がバックアップされる場合があります) 2つのICloneable
配列(1つは256アイテムの共有可能な不変配列への参照を保持し、もう1つは256アイテムの共有不可能な可変配列への参照を保持します。リストのスナップショットを作成するには、アイテムを含む内部配列のみを複製する必要があります最後のスナップショット以降に変更されました-リスト全体を複製するよりもはるかに安い可能性があります。残念ながら、標準の<!> quot; make a immutable snapshot <!> quot;インターフェイス[<=>はカウントされないことに注意してください、可変リストのクローンは可変なので、可変クローンをカプセル化することで不変のスナップショットを作成できます読み取り専用のラッパーでは、それは複製可能なものにのみ機能し、複製不可能なタイプでさえも<!> quot; mutable snapshot <!> quot;をサポートする必要があります。関数。]
お勧めは、ReadOnlyCollection <!> lt; T <!> gt;を使用して返すことです。シナリオの直接。これにより、使用法が呼び出しユーザーに明示的になります。
通常、適切なインターフェイスを使用することをお勧めします。ただし、.NET Frameworkには現在適切なIReadOnlyCollectionがないため、ReadOnlyCollectionタイプを使用する必要があります。
また、ReadOnlyCollectionは実際には読み取り専用ではないため、ReadOnlyCollectionを使用する場合は注意する必要があります。 Immutability and ReadOnlyCollection
インターフェースの使用が必要になる場合があります。おそらく、ユニットテスト中にコレクションのモックを作成するためです。を使用してReadonlyCollectionに独自のインターフェイスを追加する方法については、ブログエントリをご覧ください。アダプター。
通常は IEnumerable&lt; T&gt;
を返します。
コレクションを読み取り専用にすると( Add
、 Remove
、 Clear
などのメソッドは機能しなくなります)、コレクションはほとんど残っていません。列挙型がサポートしていないことをサポートしています- Count
と Contains
だけです、
クラスのコンシューマーが要素をコレクション内にあるかのように扱う必要がある場合、 IEnumerable
を List&lt; T&gt;
のコンストラクターに渡すのは簡単です。
T []を返す:
private ICollection<T> items;
public T[] Items
{
get { return new List<T>(items).ToArray(); }
}