web-dev-qa-db-ja.com

リスト<T>またはIList <T>

誰もが私にC#のリスト上のIListを使用したいと思う理由を私に説明できますか?

関連質問: List<T> を公開するのはなぜ悪いと考えられるのですか

472
Peanut

あなたが他の人が使用するライブラリを通してあなたのクラスを公開しているなら、あなたは一般的に具体的な実装よりもむしろインターフェースを通してそれを公開したいです。これは、後で別の具象クラスを使用するようにクラスの実装を変更することにした場合に役立ちます。その場合、あなたのライブラリのユーザはインタフェースが変わらないので彼らのコードを更新する必要はないでしょう。

あなたが内部的にそれを使っているだけなら、それほど気にする必要はないかもしれません、そしてList<T>を使っても大丈夫かもしれません。

429
tvanfosson

あまり普及していない答えは、ほとんどのプロジェクトが少数の人々によって維持されていても、インターフェース関連のサウンドバイトが問題になっているとき、あなたは自分のソフトウェアが世界中で再利用されることを装うことを好みます。あなた自身。

アーキテクチャ宇宙飛行士 。すでに.NET Frameworkにあるものに何かを追加する独自のIListを作成する可能性は非常に低いため、「ベストプラクティス」のために予約されている理論上のゼリーロットです。

Software astronauts

あなたがインタビューで使うものを尋ねられたら、明らかにあなたはIList、笑顔を言います、そして両方ともとても賢いことのためにあなた自身を喜ばせます。あるいは、一般向けAPIの場合は、IListです。うまくいけば、あなたは私の主張を得る。

300
Arec Barrwin

インターフェースは約束(または契約)です。

それは約束と常にあるので - 小さいほど良い

183
Rinat Abdullin

IList<T>の代わりに常にList<T>を使用する」と言う人もいます。
メソッドシグネチャをvoid Foo(List<T> input)からvoid Foo(IList<T> input)に変更してほしい。

これらの人々は間違っています。

それよりも微妙です。ライブラリへのパブリックインターフェイスの一部としてIList<T>を返す場合は、将来カスタムリストを作成するための興味深いオプションを残してください。そのオプションは必要ないかもしれませんが、それは議論です。具体的な型ではなく、インターフェイスを返すためのすべての引数だと思います。言及する価値はありますが、この場合、重大な欠陥があります。

マイナーな反論として、すべての呼び出し元がList<T>を必要とし、呼び出しコードが.ToList()で散らばっているのを見つけるかもしれません

しかし、はるかに重要なのは、パラメーターとしてIListを受け入れる場合は、IList<T>List<T>が同じように動作しないため、注意が必要です。名前の類似性、およびinterfaceの共有にもかかわらず、それらはnot同じものを公開しますcontract

このメソッドがあるとします:

public Foo(List<int> a)
{
    a.Add(someNumber);
}

親切な同僚がIList<int>を受け入れるようにメソッドを「リファクタリング」します。

int[]IList<int>を実装していますが、サイズは固定されているため、コードは壊れています。 ICollection<T>IList<T>のベース)のコントラクトでは、コレクションにアイテムを追加または削除する前に、IsReadOnlyフラグを確認するためにそれを使用するコードが必要です。 List<T>のコントラクトはそうではありません。

Liskov Substitution Principle(簡体字)は、派生型を追加の前提条件や事後条件なしで基本型の代わりに使用できるようにすべきだと述べています。

これは、リスコフ置換の原則に違反しているように感じます。

 int[] array = new[] {1, 2, 3};
 IList<int> ilist = array;

 ilist.Add(4); // throws System.NotSupportedException
 ilist.Insert(0, 0); // throws System.NotSupportedException
 ilist.Remove(3); // throws System.NotSupportedException
 ilist.RemoveAt(0); // throws System.NotSupportedException

しかし、そうではありません。これに対する答えは、例がIList <T>/ICollection <T>を誤って使用したことです。 ICollection <T>を使用する場合、IsReadOnlyフラグを確認する必要があります。

if (!ilist.IsReadOnly)
{
   ilist.Add(4);
   ilist.Insert(0, 0); 
   ilist.Remove(3);
   ilist.RemoveAt(0);
}
else
{
   // what were you planning to do if you were given a read only list anyway?
}

誰かが配列またはリストを渡した場合、毎回フラグをチェックしてフォールバックがあればコードは正常に動作します...しかし、実際には;誰がそれをしますか?メソッドに追加のメンバーを取得できるリストが必要かどうかを事前に知らないでください。メソッドのシグネチャでそれを指定しませんか? int[]のような読み取り専用リストが渡された場合、正確に何をするつもりでしたか?

List<T>IList<T>/ICollection<T>を使用するコードに置き換えることができます正しくcannotIList<T>/ICollection<T>List<T>を使用するコードに置き換えることができることを保証します。

具体的な型の代わりに抽象化を使用するという多くの議論の中で、単一責任原則/インターフェイス分離原則に魅力があります-可能な限り狭いインターフェイスに依存します。ほとんどの場合、List<T>を使用していて、代わりに狭いインターフェイスを使用できると考えている場合は、IEnumerable<T>を使用してください。多くの場合、アイテムを追加する必要がない場合に適しています。コレクションに追加する必要がある場合は、具体的なタイプList<T>を使用します。

私にとってIList<T>(およびICollection<T>)は.NETフレームワークの最悪の部分です。 IsReadOnlyは、最小の驚きの原則に違反しています。 Arrayなどの、アイテムの追加、挿入、または削除を許可しないクラスは、Add、Insert、およびRemoveメソッドとのインターフェースを実装しないでください。 ( https://softwareengineering.stackexchange.com/questions/306105/implementing-an-interface-when-you-dont-need-one-of-the-properties も参照してください)

IList<T>は組織に適していますか?同僚が、IList<T>の代わりにList<T>を使用するようにメソッドシグネチャを変更するように要求した場合は、IList<T>に要素を追加する方法を尋ねます。彼らがIsReadOnlyを知らない(そしてほとんどの人が知らない)場合は、IList<T>を使用しないでください。今まで。


IsReadOnlyフラグはICollection <T>から取得され、アイテムをコレクションに追加または削除できるかどうかを示すことに注意してください。しかし、本当に混乱させるために、配列の場合(IsReadOnlys == trueを返す)置き換えることができるかどうかを示していません。

IsReadOnlyの詳細については、 ICollection <T> .IsReadOnlyのmsdn定義 を参照してください。

71
perfectionist

List<T>は、IList<T>の特定の実装です。これは、整数インデックスを使用する線形配列T[]と同じ方法でアドレス指定できるコンテナです。メソッドの引数の型としてIList<T>を指定するときは、コンテナの特定の機能が必要であることだけを指定します。

例えば、インターフェース仕様は、使用される特定のデータ構造を強制しません。 List<T>の実装は、要素へのアクセス、削除、追加に関して、線形配列と同じパフォーマンスで起こります。しかし、代わりにリンクされたリストに裏打ちされた実装を想像することができます。そのために、最後に要素を追加することはより安価です(一定時間)が、ランダムアクセスはずっと高価です。 (.NETのLinkedList<T>IList<T>を実装していませんことに注意してください。)

この例では、引数リストでインターフェイスではなく実装を指定する必要がある場合もあることがわかります。この例では、特定のアクセスパフォーマンス特性が必要なときはいつでも。これは通常、コンテナの特定の実装に対して保証されています(List<T>ドキュメント:「サイズが必要に応じて動的に大きくなる配列を使用してIList<T>汎用インタフェースを実装します」)。

さらに、必要最小限の機能を公開することを検討してください。例えば。リストの内容を変更する必要がない場合は、おそらくIEnumerable<T>が拡張するIList<T>の使用を検討する必要があります。

37
ILoveFortran

具体的な実装に対してインターフェイスを使用する理由を正当化するのではなく、インターフェイスを使用するより具体的な実装を使用する理由を正当化するようにしてください。正当化できない場合は、インターフェースを使用してください。

27
Patrik Hägne

IList <T>はインターフェイスなので、List <T>を継承しなくても別のクラスを継承してIList <T>を実装することはできます。

たとえば、クラスAがあり、クラスBがそれを継承している場合は、List <T>を使用することはできません。

class A : B, IList<T> { ... }
18
Diadistis

TDDとOOPの原則は一般に、実装ではなくインタフェースへのプログラミングです。

この特定のケースでは、基本的には言語構成体について話しているので、一般的には関係ないカスタム構成体について話していません。アプリの他の部分でIListを使用したことがある場合は、Listを独自のカスタムクラスで拡張しても、リファクタリングを行わなくてもそれを渡すことができます。

これを行うためのコストは最小限です、なぜあなた自身が後で頭痛を救わないのですか?それがインターフェースの原則のすべてです。

14
annakata
public void Foo(IList<Bar> list)
{
     // Do Something with the list here.
}

この場合、IList <Bar>インターフェースを実装する任意のクラスを渡すことができます。代わりにList <Bar>を使用した場合は、List <Bar>インスタンスのみを渡すことができます。

IList <Bar>ウェイは、List <Bar>ウェイよりも疎結合です。

12
smack0007

これらのリスト対IListの質問(または回答)のどれもが署名の違いに言及していないと仮定します。 (だからこそ、私はこの質問をSOで探しました。)

それで、少なくとも.NET 4.5(2015年頃)の時点で、IListにはないListに含まれるメソッドがあります。

  • AddRange
  • AsReadOnly
  • バイナリサーチ
  • 容量
  • ConvertAll
  • 存在する
  • 見つける
  • FindAll
  • FindIndex
  • FindLast
  • FindLastIndex
  • ForEach
  • GetRange
  • InsertRange
  • LastIndexOf
  • すべて削除する
  • RemoveRange
  • ソート
  • ToArray
  • TrimExcess
  • TrueForAll
11
JasonS

実装上でインターフェースを使用するための最も重要なケースはあなたのAPIへのパラメータです。あなたのAPIがListパラメータを取る場合、それを使う人はListを使わなければなりません。パラメータの型がIListであれば、呼び出し側はもっと自由になり、今まで聞いたことのないクラスを使うことができます。

11

.NET 5.0がSystem.Collections.Generic.List<T>System.Collection.Generics.LinearList<T>に置き換えた場合はどうなりますか。 .NETは常にList<T>という名前を所有していますが、IList<T>が契約であることを保証します。だから私たちは(少なくとも私は)誰かの名前を使うことは想定されておらず(この場合は.NETです)、後で問題を起こします。

IList<T>を使用する場合、呼び出し側は常に動作することが保証されており、実装者は基礎となるコレクションをIListの代替の具体的な実装に自由に変更できます

7
Soundararajan

具体的な実装に対してインターフェイスを使用する理由に関して、すべての概念は基本的に上記の回答のほとんどで述べられています。

IList<T> defines those methods (not including extension methods)

IList<T>MSDNリンク

  1. 追加する
  2. クリア
  3. 含む
  4. コピー先
  5. GetEnumerator
  6. の指標
  7. インサート
  8. 削除する
  9. 削除する

List<T>はこれらの9つのメソッド(拡張メソッドを含まない)を実装しています。それに加えて、それは約41のパブリックメソッドを持ちます。

List<T>MSDNリンク

6
yantaq

IListやICollectionを定義すると、他のインターフェースの実装が可能になるからです。

IListまたはICollectionのいずれかで注文のコレクションを定義するIOrderRepositoryを使用することをお勧めします。 IListまたはICollectionで定義された「規則」に準拠している限り、注文のリストを提供するためにさまざまな種類の実装を持つことができます。

3
Peter Lindholm

他の投稿者のアドバイスによると、IList <>はほとんどの場合望ましいですが、 1サイクル以上のシリアル化を通じてIList <>を実行すると、.NET 3.5 sp 1 にバグがあることに注意してください。 WCF DataContractSerializerを使用した/シリアル化解除。

このバグを修正するためのSPがあります。 KB 971030

2
StuartLC

インタフェースは、少なくとも期待しているメソッドを確実に取得するようにします;インタフェースの定義を意識している、すなわち。そこにあるすべての抽象メソッドは、インタフェースを継承するクラスによって実装されます。そのため、追加機能のためにインタフェースから継承したもの以外に、いくつかのメソッドを使って独自の巨大なクラスを作成し、それらを使用しても意味がない場合は、サブクラスへの参照を使用することをお勧めします。具象クラスオブジェクトを割り当てます。

追加の利点は、具象クラスのメソッドのほんのいくつかを購読しているだけで、具象クラスへの変更からあなたのコードが安全であり、それらが具象クラスがあなたがいるインタフェースから継承する限り存在するものであるということです。使用しています。そのため、あなたにとっての安全性と、具象クラスの機能を変更または追加するための具体的な実装を書いているコーダーの自由。

2
vishy

この議論は、実装ではなくインタフェースに対してプログラムするという純粋にOOのアプローチを含む、いくつかの角度から見ることができます。この考えで、IListの使用は、最初から定義したインターフェースの受け渡しと使用と同じ原則に従います。私はまた、一般にインタフェースによって提供されるスケーラビリティと柔軟性の要因を信じる。 IList <T>を暗示するクラスを拡張または変更する必要がある場合は、消費するコードを変更する必要はありません。それはIListインターフェース契約が遵守していることを知っています。ただし、変更されたクラスに対して具象実装とList <T>を使用すると、呼び出しコードも変更する必要が生じる可能性があります。これは、IList <T>に準拠したクラスが、List <T>を使用した具象型では保証されない特定の動作を保証するためです。

また、クラスのList <T>のデフォルト実装を変更するなどの操作を行うこともできます。例えば、.Add、.Remove、またはその他のIListメソッドを実装すると、開発者にが与えられます。 )たくさんの柔軟性と力、そうでなければList <T>で事前定義

1
atconway

通常、一般的なAPIでIListを使用し(適切な場合には、リストのセマンティクスが必要な場合)、次にAPIを実装するためにListを使用するのが適切な方法です。これにより、クラスを使用するコードを壊すことなく、IListの異なる実装に変更することができます。

クラス名Listは次の.netフレームワークで変更されるかもしれませんが、インターフェースはコントラクトであるためインターフェースは決して変更されません。

あなたのAPIがforeachループなどでのみ使用されるのであれば、代わりにIEnumerableを公開することを検討した方がよいかもしれないことに注意してください。

0
lazydeveloper