IListを使用する場合とListを使用する場合


180

IListがインターフェイスで、Listが具象型であることは知っていますが、それぞれをいつ使用するかはまだわかりません。現在実行しているのは、インターフェイスを使用するSortまたはFindAllメソッドが必要ない場合です。私は正しいですか?インターフェースまたは具体的なタイプをいつ使用するかを決定するより良い方法はありますか?


1
誰かがまだ疑問に思っているなら、私はここで最良の答えを見つけます:stackoverflow.com/questions/400135/listt-or-ilistt
Crismogram

回答:


175

私が従う2つのルールがあります。

  • 機能する最も基本的なタイプを受け入れる
  • ユーザーが必要とする最もリッチなタイプを返す

したがって、コレクションを受け取る関数またはメソッドを作成するときは、リストを取得するのではなく、IList <T>、ICollection <T>、またはIEnumerable <T>を取得するように記述します。System.ObjectもTになる可能性があるため、ジェネリックインターフェイスは異種リストでも機能します。将来的にスタックやその他のデータ構造を使用することにした場合、これを行うことで頭痛を軽減できます。関数内で実行する必要があるのがそれを介したforeachだけである場合、IEnumerable <T>は実際に求めているすべてです。

一方、関数からオブジェクトを返す場合は、キャストせずに可能な限り豊富な操作セットをユーザーに提供する必要があります。したがって、その場合、それが内部でList <T>の場合は、コピーをList <T>として返します。


43
入力/出力タイプを別の方法で扱うべきではありません。入力タイプと出力タイプはどちらも、クライアントのニーズをサポートする最も基本的なタイプ(できればインターフェース)である必要があります。カプセル化は、クラスの実装についてクライアントにできるだけ少ないことを伝えることに依存しています。具体的なリストを返す場合、すべてのクライアントに再コンパイル/更新を強制せずに、他のより適切なタイプに変更することはできません。
Ash

11
私は2つのルールに同意しません...この場合、IList(より良いIEnumarable)を返す場合、ほとんどのプリミティブ型と特別な方法を使用するので、内部の関数でListを操作する必要があります。次に、「追加」または「ソート」が必要な場合は、コレクションを使用し、必要な場合はリストを使用します。したがって、私のハードルールは次のとおりです。常にIENumarableで開始し、さらに必要な場合は拡張してください
ethem

2
便宜上、「2つのルール」には名前があります。ロバストネス原理(別名、ポステルの法則)です。
easoncxz

最も基本的なタイプを返すか、最もリッチなタイプを返すかについて誰かが議論しているどちらの側でも、非常に単純化されたインターフェースを返すとき、消費するコードはif...elseisキーワードとチェーンを使用して、それよりはるかにリッチなタイプを取り出して、それにキャストして、とにかくそれを使用することになります。そのため、単にそれを不明瞭にするのではなく、基本的なインターフェースを使用して何かを非表示にすることは必ずしも保証されていません。しかし、それをより困難にすることは、消費するコードの作成者が彼らがそれをどのように使用しているかについて二度考えさせるかもしれません。
Panzercrisis 2016

6
特にこれがサービス/ APIの境界にある場合、私はポイント#2について非常に強く同意しません。変更可能なコレクションを返すと、コレクションは「ライブ」などのメソッドを呼び出しているような印象を与えることができますAdd()し、Remove()単にコレクションを超えた効果を有することができるし。のような読み取り専用インターフェースを返すIEnumerableことは、多くの場合、データ取得メソッドを実行する方法です。あなたの消費者はそれを必要に応じてより豊かなタイプに投影することができます。
STW 2016

56

FxCopによって確認されたマイクロソフトのガイドラインでは、パブリックAPIでのList <T>の使用を推奨していません-IList <T>を優先してください。

ちなみに、今ではほとんどの場合、1次元配列をIList <T>として宣言しています。つまり、Array.LengthではなくIList <T> .Countプロパティを一貫して使用できます。例えば:

public interface IMyApi
{
    IList<int> GetReadOnlyValues();
}

public class MyApiImplementation : IMyApi
{
    public IList<int> GetReadOnlyValues()
    {
        List<int> myList = new List<int>();
        ... populate list
        return myList.AsReadOnly();
    }
}
public class MyMockApiImplementationForUnitTests : IMyApi
{
    public IList<int> GetReadOnlyValues()
    {
        IList<int> testValues = new int[] { 1, 2, 3 };
        return testValues;
    }
}

3
私はこの説明/例が一番好きです!
JonH 2009年

28

人々が常に見落としているように見える重要なことがあります:

IList<T>パラメータを受け入れるものにプレーン配列を渡すことができます。その後、呼び出しIList.Add()てランタイム例外を受け取ることができます。

Unhandled Exception: System.NotSupportedException: Collection was of a fixed size.

たとえば、次のコードを考えてみます。

private void test(IList<int> list)
{
    list.Add(1);
}

次のように呼び出すと、ランタイム例外が発生します。

int[] array = new int[0];
test(array);

これは、プレーン配列をIList<T>Liskov置換の原則に違反するために発生します。

このため、電話IList<T>.Add()をかける場合は、のList<T>代わりにを要求することを検討してくださいIList<T>


これは、すべてのインターフェースに当てはまります。あなたがあなたの議論を続けたいなら、あなたはそれがいくつかの実装がスローするかもしれないので、まったくインターフェースを決して使用しないと主張することができます。あなたが、一方で、好むためにOPによって与えられた提案を検討している場合List<T>を超えるIList<T>、あなたはまた、理由を知っておく必要がありIList<T>お勧めします。(例についてblogs.msdn.microsoft.com/kcwalina/2005/09/26/...
ミカWiedenmann

3
@MichaWiedenmannここでの私の答えは、いつ電話をかけるかによって異なりますIList<T>.Add()。使用すべきではないと言っているわけではありませんIList<T> -考えられる落とし穴を指摘しています。(私は使用するIEnumerable<T>か、IReadOnlyList<T>または使用できる場合IReadOnlyCollection<T>は優先して使用する傾向がありIList<T>ます。)
マシューワトソン

24

私はパラメータをとるが戻ってこないというリーのアドバイスに同意します。

インターフェースを返すようにメソッドを指定すると、消費するメソッドに気付かれることなく、後で正確な実装を自由に変更できます。List <T>から変更する必要はないと思っていましたが、後で提供される追加機能のためにカスタムリストライブラリを使用するように変更する必要がありました。私はIList <T>しか返さなかったので、ライブラリを使用した人はコードを変更する必要がありませんでした。

もちろん、それは外部から見えるメソッド(つまり、パブリックメソッド)にのみ適用する必要があります。私は内部コードでも個人的にインターフェイスを使用していますが、重大な変更を行う場合はすべてのコードを自分で変更できるため、厳密に必要というわけではありません。


22

IEnumerable
目的に合った最も具体的でないタイプを試して使用する必要があります。
IEnumerableIList。コレクション内のアイテムをループ
するIEnumerableときに使用します。

IListが
IList実装しIEnumerableます。コレクションへのインデックスによるアクセス、要素の追加と削除などが必要な場合に
使用IListする必要があります。

リストの
List実装IList


3
優秀で明確な答え、私はそれを参考にした。ただし、ほとんどの場合、ほとんどの場合、プログラムのサイズとパフォーマンスのわずかな違いは心配する必要がありません。疑問がある場合は、リストを使用してください。
Graham Laight 2014

9

可能な限り最低の基本タイプを使用することが常に最善です。これにより、インターフェースの実装者、またはメソッドのコンシューマーに、バックグラウンドで好きなものを使用する機会が与えられます。

コレクションの場合は、可能な限りIEnumerableの使用を目指してください。これは最も柔軟性がありますが、常に適しているわけではありません。


1
可能な限り最低の基本タイプを受け入れることが常に最善です。復活は別の話です。役立つと思われるオプションを選択します。それで、あなたはあなたのクライアントがインデックス付きアクセスを使用したいと思うかもしれませんか?すでにリストであったToList()あなたの返された-ingを彼らにさせないでIEnumerable<T>IList<T>代わりに返してください。これで、クライアントは、手間をかけずに提供できるものから利益を得ることができます。
Timo

5

単一のメソッド内(または場合によっては単一のクラスまたはアセンブリ内でも)で作業していて、外部の誰もあなたが何をしているかを見ることができない場合は、Listの完全性を使用します。ただし、メソッドからリストを返す場合など、外部コードとやり取りしている場合は、特定の実装に縛られることなく、インターフェースを宣言するだけで済みます。特に、誰に対してコンパイルするかを制御できない場合その後コード。具象型から始めて、別の型に変更することを決めた場合、同じインターフェースを使用していても、インターフェースまたは抽象基本型から始めない限り、他の誰かのコードを壊すことになります。


4

私はこの種のことについて厳格な規則はないと思いますが、私は通常、絶対的に必要になるまで、可能な限り軽い方法を使用するというガイドラインに従っています。

たとえば、PersonクラスとクラスがあるとしますGroupGroupインスタンスは、多くの人々を持っているので、ここではリストは意味をなさないと思います。でリストオブジェクトを宣言すると、Groupを使用してIList<Person>としてインスタンス化しますList

public class Group {
  private IList<Person> people;

  public Group() {
    this.people = new List<Person>();
  }
}

そして、もしあなたがすべてを必要としさえしなければ、IListあなたもいつでも使うことができIEnumerableます。最近のコンパイラとプロセッサでは、速度の違いはないと思いますので、これは単なるスタイルの問題です。


3
そもそもそれを単なるリストにしてみませんか?あなたがIListのそれを作るから得るボーナスはその後、コンストラクタであなたはリスト<>の中にそれを作る、なぜ私はまだ理解していない
chobo2

私が同意するのは、List <T>オブジェクトを明示的に作成している場合、インターフェースの利点を失うということですか。
The_Butcher 2017

4

ほとんどの場合、最も一般的に使用可能なタイプ、この場合はIList、またはさらに優れたIEnumerableインターフェイスを使用する方が優れているため、後で実装を簡単に切り替えることができます。

ただし、.NET 2.0では、煩わしいことがあり、IListにはSort()メソッドがありません。代わりに、付属のアダプターを使用できます。

ArrayList.Adapter(list).Sort()

2

リストがList以外のIList実装にキャストされる場合など、必要な場合にのみインターフェイスを使用する必要があります。これは、たとえば、データを取得するときにIListsをNHibernateバッグオブジェクトにキャストするNHibernateを使用する場合に当てはまります。

Listが特定のコレクションで使用する唯一の実装である場合は、それを具体的なList実装として宣言してください。


1

通常遭遇する状況では、IListを直接使用することはほとんどありません。

通常はメソッドの引数として使用します

void ProcessArrayData(IList almostAnyTypeOfArray)
{
    // Do some stuff with the IList array
}

これにより、時々発生するIListではなくIEnumerableを使用しない限り、.NETフレームワークのほとんどすべての配列に対して汎用処理を実行できます。

それは本当に必要な機能の種類に帰着します。ほとんどの場合、Listクラスを使用することをお勧めします。IListは、コレクション内にカプセル化したい非常に特定のルールを持つカスタム配列を作成する必要がある場合に最適です。これにより、自分自身を繰り返さずに、.NETでそれをリストとして認識させることができます。


1

AListオブジェクトを使用すると、リストの作成、リストへの追加、削除、更新、インデックス作成などを行うことができます。リストは、オブジェクトタイプを指定する汎用リストが必要な場合にのみ使用されます。

一方、IListはインターフェイスです。基本的に、独自のタイプのリスト、たとえばBookListというリストクラスを作成する場合は、インターフェイスを使用して、新しいクラスに基本的なメソッドと構造を提供できます。IListは、Listを実装する独自の特別なサブクラスを作成する場合に使用します。

もう1つの違いは、IListはインターフェイスであり、インスタンス化できないことです。リストはクラスであり、インスタンス化できます。その意味は:

IList<string> MyList = new IList<string>();

List<string> MyList = new List<string>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.