web-dev-qa-db-ja.com

IList <T>ではなく、常にIEnumerable <T>を返す必要がありますか?

DALまたはアイテムのセットを返す他のコードを作成する場合、常にreturnステートメントを作成する必要があります。

public IEnumerable<FooBar> GetRecentItems()

または

public IList<FooBar> GetRecentItems()

現在、私のコードでは、可能な限りIEnumerableを使用しようとしていますが、これがベストプラクティスかどうかはわかりません。私が最も一般的なデータ型を返しながら、それが何をするのかを説明しているので、それは正しいように思えましたが、おそらくこれは正しくないでしょう。

92
KingNestor

それは、その特定のインターフェイスを使用している理由に大きく依存します。

たとえば、_IList<T>_には、_IEnumerable<T>_にはないメソッドがいくつかあります。

  • IndexOf(T item)
  • Insert(int index, T item)
  • RemoveAt(int index)

およびプロパティ:

  • _T this[int index] { get; set; }_

何らかの方法でこれらのメソッドが必要な場合は、必ず_IList<T>_を返します。

また、_IEnumerable<T>_の結果を使用するメソッドが_IList<T>_を予期している場合、CLRが必要な変換を考慮しないようにし、コンパイルされたコードを最適化します。

42
Jon Limjap

フレームワークの設計ガイドラインでは、呼び出し元によって変更可能なコレクションを返す必要がある場合はクラス Collection を使用するか、読み取り専用コレクションの場合は ReadOnlyCollection を使用することをお勧めします。

これが単純なIListよりも好ましい理由は、IListが読み取り専用かどうかを呼び出し側に通知しないためです。

代わりにIEnumerable<T>を返す場合、特定の操作は呼び出し側が実行するのが少し難しいかもしれません。また、呼び出し側にコレクションを変更する柔軟性を与えることはもうありません。これはあなたが望むかもしれないし、望まないかもしれません。

LINQにはいくつかのトリックがあり、実行されるタイプに基づいて特定の呼び出しを最適化することに注意してください。したがって、たとえば、Countを実行し、基になるコレクションがListである場合、すべての要素をウォークスルーすることはありません。

個人的には、ORMの場合、おそらく戻り値としてCollection<T>を使い続けるでしょう。

63
Sam Saffron

一般に、最も一般的なものを要求し、可能な限り最も具体的なものを返す必要があります。そのため、パラメーターを受け取るメソッドがあり、IEnumerableで利用できるものだけが本当に必要な場合は、それがパラメータータイプになります。メソッドがIListまたはIEnumerableを返すことができる場合は、IListを返すことをお勧めします。これにより、最も幅広い消費者が使用できるようになります。

必要なものは緩く、提供するものは明確にします。

20
Mel

場合によります...

最小の派生型(IEnumerable)を返すと、基礎となる実装を変更するための最大限の余裕が残ります。

より派生した型(IList)を返すと、APIのユーザーに結果に対する操作が増えます。

私は常に、ユーザーが必要とするすべての操作を含む最小派生型を返すことをお勧めします...したがって、基本的には、定義するAPIのコンテキストで、結果の操作が意味を持つものを削除する必要があります。

19
jerryjvl

考慮すべきことの1つは、遅延実行LINQステートメントを使用して_IEnumerable<T>_を生成している場合、メソッドから戻る前に.ToList()を呼び出すと、アイテムが2回繰り返される可能性があることです。リストを作成し、呼び出し元が戻り値をループ、フィルター、または変換するときに1回作成します。実用的な場合は、必要になるまでLINQ-to-Objectsの結果を具体的なリストまたは辞書に変換しないようにします。呼び出し元がListを必要とする場合、それは1つの簡単なメソッド呼び出しです-それらのためにその決定をする必要はありません、そしてそれは呼び出し元がforeachをしている場合に私のコードをわずかにより効率的にします。

10
Joel Mueller

List<T>は、返されるオブジェクトの変更やインデックスによるアクセスなど、呼び出しコードにさらに多くの機能を提供します。したがって、質問は次のように要約されます:アプリケーションの特定のユースケースでは、呼び出し元の利便性のために、(おそらく新しく作成されたコレクションを返すことによって)そのような使用をサポートしたいですか?または、すべての場合のシンプルなケースの速度が必要ですか?呼び出し側のニーズはコレクションをループすることであり、これが誤って変更されるなどを恐れることなく、実際の基になるコレクションへの参照を安全に返すことができますか?

この質問に答えることができるのは、呼び出し側が戻り値で何をしたいのか、パフォーマンスがここでどれほど重要か(コピーするコレクションの大きさ、これがボトルネックになる可能性、等)。

8
Alex Martelli

どちらでも使用できると思いますが、それぞれに用途があります。基本的にListIEnumerableですが、カウント機能、要素の追加、要素の削除があります

IEnumerableは要素のカウントには効率的ではありません

コレクションが読み取り専用であることを意図している場合、またはコレクションの変更がParentによって制御されている場合、IListに対してだけCountを返すことはお勧めできません。

Linqでは、Count()拡張メソッドが_IEnumerable<T>_にあり、CLR内では、基になる型がIListである場合に_.Count_にショートカットするため、パフォーマンスの違いは無視できる。

一般的に(意見)、可能な場合はIEnumerableを返すことをお勧めします。追加が必要な場合はこれらのメソッドを親クラスに追加します。そうでない場合、消費者はモデル内で原則に違反するコレクションを管理しています、例えばmanufacturer.Models.Add(model)はデメテルの法則に違反しています。もちろん、これらは単なるガイドラインであり、厳格な規則ではありませんが、適用可能性を完全に把握するまで、盲目的に従うことは、まったく従わないことよりも優れています。

_public interface IManufacturer 
{
     IEnumerable<Model> Models {get;}
     void AddModel(Model model);
}
_

(注:nNHibernateを使用する場合、異なるアクセサーを使用してプライベートIListにマップする必要がある場合があります。)

4
user53791

入力パラメーターの代わりに戻り値について話すときは、それほど簡単ではありません。入力パラメーターの場合、何をする必要があるかを正確に知っています。したがって、コレクションを反復処理する必要がある場合はIEnumberableを使用し、追加または削除する必要がある場合はIListを使用します。

戻り値の場合、それはより困難です。あなたの発信者は何を期待していますか? IEnumerableを返す場合、彼はそれからIListを作成できることをアプリオリに知りません。ただし、IListを返すと、IListを反復処理できることがわかります。そのため、発信者がデータをどのように処理するかを考慮する必要があります。呼び出し元が必要とする/期待する機能は、何を返すかを決定するときに管理する必要があるものです。

1
JP Alioto

どちらでも使用できると思いますが、それぞれに用途があります。基本的にListIEnumerableですが、カウント機能、要素の追加、要素の削除があります

IEnumerableは、要素のカウントやコレクション内の特定の要素の取得には効率的ではありません。

Listは、特定の要素の検索、要素の追加や削除が簡単なコレクションに最適です。

一般的に、可能な限りListを使用しようとします。これにより柔軟性が増します。

List<FooBar> getRecentItems()ではなくIList<FooBar> GetRecentItems()を使用します

0
Stuart

すべてが言っているように、呼び出し層で機能を追加/削除したくない場合は、IEnumerableに投票します。これは、デザインの観点で私が好きな反復と基本的な機能のみを提供するためです。 IListを返すと、私の票は常に再選されますが、それは主にあなたが好きなものとそうでないものです。パフォーマンスの観点からは、それらはもっと同じだと思います。

0
Usman Masood

外部コードをカウントしない場合は、IEnumerableを返すことをお勧めします。たとえば、後でyield iteratorロジックのように実装を変更して(外部コードに影響を与えることなく)メモリリソースを節約できるためです(ちなみに非常に良い言語機能)。

ただし、アイテムのカウントが必要な場合は、IEnumerableとIList -ICollectionの間に別のレイヤーがあることを忘れないでください。

0
arbiter

私はここで少し離れているかもしれませんが、これまで誰もそれを提案していなかったのを見て、なぜあなたは(I)Collection<T>を返さないのですか?

私が覚えていることから、Collection<T>List<T>よりも好ましい戻り型でした。これは実装を抽象化するためです。それらはすべてIEnumerableを実装していますが、それは仕事には少し低すぎるように思えます。

0
Tiberiu Ana

一般的なルールは、より具体的なクラスを使用して戻ることであり、不要な作業を行わずに、呼び出し元により多くのオプションを与えることです。

そうは言っても、あなたが書いているあなたの目の前のコードを考慮することは、次の人が(理由の範囲内で)書くコードよりも重要だと思います。これは、すでに存在するコードについて仮定することができるからです。

インターフェイスでIEnumerableからコレクションに上に移動すると機能し、コレクションからIEnumerableに下に移動すると既存のコードが破損することに注意してください。

これらの意見がすべて矛盾しているように見える場合、それは決定が主観的であるためです。

0
Sprague