'IList'対 'ICollection'対 'Collection'を返す


119

パブリックAPIのメソッドとプロパティからどのコレクション型を返すべきか混乱しています。

私が念頭に置いているコレクションはIListICollectionCollectionです。

これらのタイプの1つを返すことは常に他のタイプよりも優先されますか、それとも特定の状況に依存しますか?



回答:


71

一般に、可能な限り一般的なタイプ、つまり、コンシューマが使用する必要がある、返されたデータを十分に把握しているタイプを返す必要があります。これにより、APIを使用しているコードを壊すことなく、APIの実装をより自由に変更できます。

IEnumerable<T>インターフェースも戻り値の型と見なしてください。結果が反復されるだけの場合、消費者はそれ以上必要としません。


25
ただし、Countプロパティを含む追加のユーティリティを提供するためにIEnumerable <T>を拡張するICollection <T>も検討してください。シーケンスを複数回走査することなくシーケンスの長さを決定できるため、これは役立ちます。
レトロドローン

11
@retrodrone:実際には、Countメソッドの実装はコレクションの実際のタイプをチェックするため、配列などのいくつかの既知のタイプのLengthまたはCountプロパティを使用しICollectionますIEnumerable
グッファ2013

8
@Guffa:クラスの場合ことに注意することは価値があるかもしれThing<T>道具IList<T>ではなく、非ジェネリックICollection、その後の呼び出しIEnumerable<Cat>.Count()にはThing<Cat>速くなりますが、呼び出し元がIEnumerable<Animal>.Count()拡張メソッドを見つけるために見て、ではないであろうから、(の実装遅くなりますICollection<Cat>)。ICollectionただし、クラスがnon-genericを実装している場合は、それIEnumerable<Animal>.Countを見つけて使用します。
スーパーキャット2013


8
同意しません。必要な場合はクライアントが直接使用できるように、最もリッチなタイプを返すことが最善です。List <>がある場合、なぜ呼び出し側に不必要にToList()を呼び出させるためだけにIEnuerable <>を返すのですか?
zumalifeguard 2016年

140

ICollection<T>以下のようなコレクションのセマンティクスを公開するインターフェイスがありAdd()Remove()Count

Collection<T>ICollection<T>インターフェースの具体的な実装です。

IList<T>本質的にICollection<T>はランダムな順序ベースのアクセスです。

この場合、結果に順序ベースのインデックス付けなどのリストのセマンティクスがIList<T>必要かどうか(次に使用)、または結果の順序付けされていない「バッグ」を返す必要があるかどうか(その後に使用)を決定する必要がありますICollection<T>


5
Collection<T>実装するIList<T>だけでなくICollection<T>
CodesInChaos

56
.Net内のすべてのコレクションIEnumerable<T>は、順序付けされているため、順序付けされています。どのような区別IList<T>からすると、ICollection<T>それがインデックスで動作するようにメンバーを提供することです。たとえば、list[5]動作しますが、collection[5]コンパイルされません。
スリック

5
また、順序付けられたコレクションを実装する必要があることにも同意しませんIList<T>。順序付けられていないコレクションがたくさんあります。IList<T>高速インデックスアクセスについてです。
CodesInChaos

4
@yoyo .netコレクションのクラスとインターフェースは、単に設計が悪く、名前が不適切です。彼らがなぜそのようにそれらを命名したのかについて私はあまり考えません。
CodesInChaos 2013年

6
@svick:注文されているため、すべてのコレクションIEnumerable<T>が注文されていますか?取るHashSet<T>-それは実装されますIEnumerable<T>が、明らかに注文されていません。つまり、アイテムの順序には影響がなく、内部ハッシュテーブルが再編成された場合、この順序はいつでも変更される可能性があります。
Olivier Jacot-Descombes、2015年

61

主な違いIList<T>とはICollection<T>つまりIList<T>、あなたがインデックスを経由してアクセス要素にできます。IList<T>配列のような型を記述します。の要素には、ICollection<T>列挙によってのみアクセスできます。どちらも要素の挿入と削除ができます。

コレクションを列挙するだけでよい場合IEnumerable<T>は、が推奨されます。他の2つの利点があります。

  1. コレクションへの変更を許可しません(参照タイプの場合、要素への変更は許可しません)。

  2. アルゴリズムによって生成され、コレクションではない列挙を含む、可能な限り最大のさまざまなソースを許可します。

Collection<T>コレクションの実装者に主に役立つ基本クラスです。インターフェース(API)で公開すると、そこから派生しない多くの有用なコレクションが除外されます。


欠点の1つIList<T>は、配列がそれを実装しているが、項目を追加または削除できないことです(つまり、配列の長さを変更できない)。IList<T>.Add(item)配列を呼び出すと、例外がスローされます。IList<T>ブール型のプロパティがIsReadOnlyあるので、そうする前にチェックできるので、状況はやや混乱しています。しかし、私の目には、これはまだライブラリの設計上の欠陥です。したがって、List<T>アイテムを追加または削除する可能性が必要な場合は、直接使用します。


コード分​​析(およびFxCop)は、具象ジェネリックコレクションを返す場合、次のいずれかを返すようにアドバイスしていますCollectionReadOnlyCollectionまたはKeyedCollection。そして、それListは返されるべきではありません。
DavidRR

@DavidRR:多くの場合、アイテムを追加または削除する必要があるメソッドにリストを渡すと便利です。パラメータをと入力した場合IList<T>、メソッドが配列をパラメータとして呼び出されないようにする方法はありません。
Olivier Jacot-Descombes 2017

7

IList<T>すべての総称リストの基本インターフェースです。これは順序付けられたコレクションであるため、実装は、ソートされた順序から挿入順序まで、順序を決定できます。さらにIlist、メソッドがインデックスに基づいてリストのエントリを読み取り、編集できるようにするItemプロパティがあります。これにより、位置インデックスのリストに値を挿入したり、リストから値を削除したりできます。

また、からIList<T> : ICollection<T>、のすべてのメソッドICollection<T>もここで実装に使用できます。

ICollection<T>すべてのジェネリックコレクションの基本インターフェイスです。サイズ、列挙子、同期方法を定義します。コレクションに項目を追加または削除することはできますが、インデックスプロパティがないため、それが発生する位置を選択することはできません。

Collection<T>以下のための実装を提供しIList<T>IListそしてIReadOnlyList<T>

ICollection<T>代わりにIList<T>、より狭いインターフェイスタイプを使用すると、破壊的な変更からコードを保護できます。のようなより広いインターフェイスタイプを使用するとIList<T>、コードの変更を壊す危険性が高まります。

ソースからの引用、

ICollectionICollection<T>:コレクションを変更するか、コレクションのサイズを気にします。 IListIList<T>:コレクションを変更し、コレクション内の要素の順序や配置に注意します。


5

インターフェースタイプを返すことはより一般的であるため、(特定のユースケースに関する詳細な情報を欠いている)私はそれに傾倒します。インデックス作成のサポートを公開するIList<T>場合ICollection<T>は、を選択します。それ以外の場合は十分です。最後に、返された型が読み取り専用であることを示すには、を選択しますIEnumerable<T>

また、これまでに読んだことがない場合は、Brad AbramsとKrzysztof Cwalinaが「再利用可能な.NETライブラリのフレームワーク設計ガイドライン:慣習、慣用句、およびパターン」というタイトルの優れた本を執筆しました(ここからダイジェストをダウンロードできます)。


IEnumerable<T>読み取り専用ですか?もちろん、リポジトリのコンテキストで再調整する場合、ToListを実行した後でもスレッドセーフではありません。問題は、元のデータソースがGCを取得したときに、IEnumarble.ToList()がマルチスレッドコンテキストで機能しないことです。それはあなたが仕事をした後にGCが呼び出されるので線形1で動作しますが、async現在4.5+ IEnumarbaleでの日数を使用することは簡単ではありません。
Piotr Kula 2017

-3

この質問から来るいくつかの主題があります:

  • インターフェースとクラス
  • いくつかの類似したクラス、コレクション、リスト、配列から、どの特定のクラスですか?
  • 一般的なクラスとサブアイテム(「ジェネリック」)のコレクション

オブジェクト指向APIであることを強調したい場合があります

インターフェースとクラス

インターフェースの経験があまりない場合は、クラスを使用することをお勧めします。必要ではないにせよ、多くの場合、開発者はインターフェースにジャンプします。

そして、良いクラスデザインの代わりに、質の悪いインターフェイスデザインをやめることです。ちなみに、最終的には優れたインターフェイスデザインに移行できます...

APIには多くのインターフェースが表示されますが、必要がなければ、急いでそれを実行しないでください。

最終的には、インターフェースをコードに適用する方法を学びます。

いくつかの類似したクラス、コレクション、リスト、配列から、どの特定のクラスですか?

c#(dotnet)には、交換可能なクラスがいくつかあります。すでに述べたように、「CanBeSortedClass」などのより具体的なクラスから何かが必要な場合は、APIで明示的にしてください。

APIユーザーは、クラスを並べ替えたり、要素に何らかの形式を適用したりできることを本当に知っている必要がありますか?次に、「CanBeSortedClass」または「ElementsCanBePaintedClass」を使用します。それ以外の場合は、「GenericBrandClass」を使用します。

それ以外の場合は、より一般的なクラスを使用します。

一般的なコレクションクラスとサブアイテム(「ジェネリック」)コレクション

他の要素を含むクラスがあり、すべての要素が特定のタイプであることを指定できます。

ジェネリックコレクションは、あなたがこのように、それぞれの新しいサブ項目タイプのために、新しいコレクションを作成することなく、いくつかのコードアプリケーションのために、同じコレクションを使用することができ、それらのクラスです:コレクション

APIユーザーは、すべての要素に対して同じように、非常に具体的なタイプを必要とするでしょうか?

のようなものを使用してくださいList<WashingtonApple>

あなたのAPIユーザーはいくつかの関連するタイプを必要とするでしょうか?

公開List<Fruit>あなたのAPIのために、使用List<Orange> List<Banana>List<Strawberry>内部的に、どこOrangeBananaStrawberryの子孫ですFruit

あなたのAPIユーザーはジェネリック型コレクションを必要としていますか?

使用Listすべての項目があり、object(S)。

乾杯。


7
「インターフェースの経験があまりない場合は、クラスを使用することをお勧めします」これは良いアドバイスではありません。わからないことがあったら近づかないで。インターフェースは非常に強力であり、「抽象化に対するプログラム対具体化へのプログラム」の概念をバックアップします。モックを介してタイプを交換できるため、インターフェースはユニットテストを実行するためにも非常に強力です。これらのコメント以外にもインターフェースを使用する理由はたくさんありますので、ぜひぜひご検討ください。
atconway 2013

@atconway私は定期的にインターフェースを使用していますが、多くの概念として、それは強力なツールであり、混合して使用するのは簡単です。そして、時には、開発者はそれを必要としないかもしれません。私の提案は、「インターフェイスを使用しない」ではなく、「この場合、インターフェイスをスキップできます。それについて学習し、後でそれらを最大限に活用することができます」でした。乾杯。
umlcat 2013

1
常にインターフェースにプログラミングすることをお勧めします。コードの疎結合を維持するのに役立ちます。
mac10688 2014

@ mac10688私の以前の回答(atconway)もコメントに適用されます。
umlcat 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.