回答:
私が従う2つのルールがあります。
したがって、コレクションを受け取る関数またはメソッドを作成するときは、リストを取得するのではなく、IList <T>、ICollection <T>、またはIEnumerable <T>を取得するように記述します。System.ObjectもTになる可能性があるため、ジェネリックインターフェイスは異種リストでも機能します。将来的にスタックやその他のデータ構造を使用することにした場合、これを行うことで頭痛を軽減できます。関数内で実行する必要があるのがそれを介したforeachだけである場合、IEnumerable <T>は実際に求めているすべてです。
一方、関数からオブジェクトを返す場合は、キャストせずに可能な限り豊富な操作セットをユーザーに提供する必要があります。したがって、その場合、それが内部でList <T>の場合は、コピーをList <T>として返します。
if...else
、is
キーワードとチェーンを使用して、それよりはるかにリッチなタイプを取り出して、それにキャストして、とにかくそれを使用することになります。そのため、単にそれを不明瞭にするのではなく、基本的なインターフェースを使用して何かを非表示にすることは必ずしも保証されていません。しかし、それをより困難にすることは、消費するコードの作成者が彼らがそれをどのように使用しているかについて二度考えさせるかもしれません。
Add()
し、Remove()
単にコレクションを超えた効果を有することができるし。のような読み取り専用インターフェースを返すIEnumerable
ことは、多くの場合、データ取得メソッドを実行する方法です。あなたの消費者はそれを必要に応じてより豊かなタイプに投影することができます。
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;
}
}
人々が常に見落としているように見える重要なことがあります:
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>
。
List<T>
を超えるIList<T>
、あなたはまた、理由を知っておく必要がありIList<T>
お勧めします。(例についてblogs.msdn.microsoft.com/kcwalina/2005/09/26/...)
IList<T>.Add()
。使用すべきではないと言っているわけではありませんIList<T>
-考えられる落とし穴を指摘しています。(私は使用するIEnumerable<T>
か、IReadOnlyList<T>
または使用できる場合IReadOnlyCollection<T>
は優先して使用する傾向がありIList<T>
ます。)
私はパラメータをとるが戻ってこないというリーのアドバイスに同意します。
インターフェースを返すようにメソッドを指定すると、消費するメソッドに気付かれることなく、後で正確な実装を自由に変更できます。List <T>から変更する必要はないと思っていましたが、後で提供される追加機能のためにカスタムリストライブラリを使用するように変更する必要がありました。私はIList <T>しか返さなかったので、ライブラリを使用した人はコードを変更する必要がありませんでした。
もちろん、それは外部から見えるメソッド(つまり、パブリックメソッド)にのみ適用する必要があります。私は内部コードでも個人的にインターフェイスを使用していますが、重大な変更を行う場合はすべてのコードを自分で変更できるため、厳密に必要というわけではありません。
IEnumerable
目的に合った最も具体的でないタイプを試して使用する必要があります。
IEnumerable
はIList
。コレクション内のアイテムをループ
するIEnumerable
ときに使用します。
IListが
IList
実装しIEnumerable
ます。コレクションへのインデックスによるアクセス、要素の追加と削除などが必要な場合に
使用IList
する必要があります。
リストの
List
実装IList
。
可能な限り最低の基本タイプを使用することが常に最善です。これにより、インターフェースの実装者、またはメソッドのコンシューマーに、バックグラウンドで好きなものを使用する機会が与えられます。
コレクションの場合は、可能な限りIEnumerableの使用を目指してください。これは最も柔軟性がありますが、常に適しているわけではありません。
ToList()
あなたの返された-ingを彼らにさせないでIEnumerable<T>
、IList<T>
代わりに返してください。これで、クライアントは、手間をかけずに提供できるものから利益を得ることができます。
単一のメソッド内(または場合によっては単一のクラスまたはアセンブリ内でも)で作業していて、外部の誰もあなたが何をしているかを見ることができない場合は、Listの完全性を使用します。ただし、メソッドからリストを返す場合など、外部コードとやり取りしている場合は、特定の実装に縛られることなく、インターフェースを宣言するだけで済みます。特に、誰に対してコンパイルするかを制御できない場合その後コード。具象型から始めて、別の型に変更することを決めた場合、同じインターフェースを使用していても、インターフェースまたは抽象基本型から始めない限り、他の誰かのコードを壊すことになります。
私はこの種のことについて厳格な規則はないと思いますが、私は通常、絶対的に必要になるまで、可能な限り軽い方法を使用するというガイドラインに従っています。
たとえば、Person
クラスとクラスがあるとしますGroup
。Group
インスタンスは、多くの人々を持っているので、ここではリストは意味をなさないと思います。でリストオブジェクトを宣言すると、Group
を使用してIList<Person>
としてインスタンス化しますList
。
public class Group {
private IList<Person> people;
public Group() {
this.people = new List<Person>();
}
}
そして、もしあなたがすべてを必要としさえしなければ、IList
あなたもいつでも使うことができIEnumerable
ます。最近のコンパイラとプロセッサでは、速度の違いはないと思いますので、これは単なるスタイルの問題です。
通常遭遇する状況では、IListを直接使用することはほとんどありません。
通常はメソッドの引数として使用します
void ProcessArrayData(IList almostAnyTypeOfArray)
{
// Do some stuff with the IList array
}
これにより、時々発生するIListではなくIEnumerableを使用しない限り、.NETフレームワークのほとんどすべての配列に対して汎用処理を実行できます。
それは本当に必要な機能の種類に帰着します。ほとんどの場合、Listクラスを使用することをお勧めします。IListは、コレクション内にカプセル化したい非常に特定のルールを持つカスタム配列を作成する必要がある場合に最適です。これにより、自分自身を繰り返さずに、.NETでそれをリストとして認識させることができます。
AListオブジェクトを使用すると、リストの作成、リストへの追加、削除、更新、インデックス作成などを行うことができます。リストは、オブジェクトタイプを指定する汎用リストが必要な場合にのみ使用されます。
一方、IListはインターフェイスです。基本的に、独自のタイプのリスト、たとえばBookListというリストクラスを作成する場合は、インターフェイスを使用して、新しいクラスに基本的なメソッドと構造を提供できます。IListは、Listを実装する独自の特別なサブクラスを作成する場合に使用します。
もう1つの違いは、IListはインターフェイスであり、インスタンス化できないことです。リストはクラスであり、インスタンス化できます。その意味は:
IList<string> MyList = new IList<string>();
List<string> MyList = new List<string>