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


97

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

public IEnumerable<FooBar> GetRecentItems()

または

public IList<FooBar> GetRecentItems()

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


1
それはあなたが求めているList <T>またはIList <T>ですか?タイトルと質問は別のことを言っています...
FredrikMörk

可能な場合はいつでも、concerteタイプのユーザーインターフェイスIEnumerableまたはIlist instaead。
ウスマンマスード、

2
コレクションをList <T>として返します。あなたがそのリスト<T>から抽出することができますので、私はIEnumberable <T>を返すために必要が表示されない
チャック・コンウェイ


回答:


44

それは、その特定のインターフェースを使用する理由に本当に依存します。

たとえば、にIList<T>は存在しないメソッドがいくつかありますIEnumerable<T>

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

およびプロパティ:

  • T this[int index] { get; set; }

これらのメソッドが何らかの方法で必要な場合は、必ずreturnしてくださいIList<T>

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


2
@Jon FDGは、コレクション型の戻り値としてCollection <T>またはReadOnlyCollection <T>を使用することを推奨しています。私の答えを参照してください。
サムサフラン

「CLRを保存します」と言ったときの最後の文の意味が明確ではありません。IEnumerableとIListを使用して何を保存しますか?あなたはそれをより明確にすることができますか?
PositiveGuy

1
@CoffeeAddictこの回答から3年後、私はあなたが正しいと思います-その最後の部分はあいまいです。IList <T>をパラメーターとして期待するメソッドがIEnumerable <T>を取得する場合、IEnumerableを新しいList <T>または他のIList <T>インプリメンターに手動でラップする必要があり、その作業はあなたのためのCLR。反対-のIEnumerable <T>を期待するメソッドIList <T>を取得は、あり、いくつかのアンボクシングをしなければならないが、後知恵であるためのIList <T>を実装してIEnumerable <T>に必要ないかもしれません。
Jon Limjap

68

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

これは単純に好まれる理由は、IListつまりIListそのだけか読めば、発信者に通知しません。

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

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

個人的には、ORMの場合、おそらくCollection<T>戻り値として使用します。


12
コレクションガイドラインには、DOおよびDONTのより詳細なリストが含まれています。
Chaquotay 2016年

26

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

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


1
私は反対の結論に達しました:特定のタイプを受け入れ、一般的なタイプを返す必要があるということです。しかし、より広く適用可能な方法の方が、より制約のある方法よりも優れているということは正しいかもしれません。これについてもっと考えなければなりません。
Dave Cousineau、2011年

3
一般的なタイプを入力として受け入れる正当な理由は、コンポーネントから可能な限り多くの再利用を得るために、可能な限り幅広い入力で作業できることです。一方、手元にあるオブジェクトの種類はすでに正確にわかっているので、それをマスクしても意味がありません。
Mel

1
より一般的なパラメータを使用することに同意すると思いますが、一般的でないものを返す理由は何ですか?
Dave Cousineau、2011年

6
では、別の方法で試してみましょう。なぜ情報を捨てるのですか?結果がIEnumerable <T>であることにのみ関心がある場合、それがIList <T>であることを知るのになんらかの害を及ぼしますか?いいえ、ありません。場合によっては余分な情報になることもありますが、害はありません。利益のために。リストまたはIListを返すと、コレクションが既に取得されていることがすぐにわかります。これは、IEnumerableではわかりません。これは役に立つ情報かもしれませんし、そうでないかもしれませんが、もう一度、なぜ情報を捨てるのですか?何かについて追加の情報を知っている場合は、伝えてください。
Mel

振り返ってみると、誰も私に電話をかけてきたことに驚いています。IEnumerable WOULDは既に取得されています。ただし、IQueryableは列挙されていない状態で返される可能性があります。したがって、おそらくより良い例は、IQueryableとIEnumerableを返すでしょう。より具体的なIQueryableを返すとさらに構成が可能になりますが、IEnumerableを返すとすぐに列挙が強制され、メソッドの構成可能性が低下します。
Mel

23

場合によります...

最小の派生型(IEnumerable)を返すことで、基礎となる実装を変更するための最も余裕ができます。

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

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


他の方法にも当てはまる一般的な答えです。
user420667

1
私はこの回答が非常に古いことを知っています...しかしそれはドキュメントと矛盾するようです:「IDictionary <TKey、TValue>インターフェースもIList <T>インターフェースも必要なコレクションの要件を満たさない場合、新しいコレクションクラスを代わりに、ICollection <T>インターフェースを使用すると柔軟性が向上します。(msdn.microsoft.com/en-us/library/92t2ye13 (
v

11

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


@ジョエル・ミュラー、私は通常、とにかくそれらに対してToList()を呼び出します。私は通常、IQueryableを残りのプロジェクトに公開したくありません。
KingNestor 2009

4
私はLINQ-to-Objectsについてより多く言及していましたが、IQueryableは一般的にはこの図には入りません。データベースが関係している場合は、ToList()がさらに必要になります。そうしないと、反復する前に接続を閉じるリスクがあり、うまく機能しません。ただし、それが問題ではない場合、IQueryableを非表示にするときに追加の反復を強制せずにIQueryableをIEnumerableとして公開するのは非常に簡単です。
Joel Mueller、

9

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

あなただけがこの質問に答えることができ、呼び出し元が戻り値で何をしたいのか、そしてここでパフォーマンスの重要性を理解することによってのみ(コピーするコレクションの大きさ、これがボトルネックになる可能性がどれくらいあるか、等)。


「これが誤って変更されるのを恐れることなく、実際の基になるコレクションへの参照を安全に返す」-IEnumerable <T>を返しても、List <T>にキャストして変更しなおすことはできませんか?
コビ

すべてのIEnumarable <T>がList <T>でもあるわけではありません。返されたオブジェクトがList <T>から継承する型ではない場合、またはIList <T>を実装する場合、これによりInvalidCastExceptionが発生します。
ローグライダー2009

2
List <T>には、特定の実装にロックされるという問題があります。Collection<T>またはReadOnlyCollection <T>が推奨されます
Sam Saffron

4

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

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

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

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

一般的に私は(意見)あなたが、その後の追加を行う親クラスにこれらのメソッドを追加する必要がある場合など、それ以外の消費者は、その後の原則に違反するモデル内のコレクションを管理している、可能性のIEnumerableを返すために、より良い練習と感じるmanufacturer.Models.Add(model)の法律に違反しますデメテル。もちろん、これらは単なるガイドラインであり、厳密な規則ではありませんが、適用性を完全に理解するまでは、盲目的にフォローすることは、まったくフォローしないよりも優れています。

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

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


2

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

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


0

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


0

外部コードでカウントしない場合は、後でIEnumerableを返すことをお勧めします。これは、たとえば、外部コードに影響を与えずに実装を変更して、たとえば、yieldイテレータロジックやメモリリソースを節約できるためです(ところで、非常に優れた言語機能) )。

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


0

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

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


0

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

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

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

List柔軟性を高めるため、通常は可能な限り使用するようにしています。

List<FooBar> getRecentItems() ではなく 使用 IList<FooBar> GetRecentItems()


0

一般的なルールは、より具体的なクラスを使用して戻り、不要な作業を行わないようにし、呼び出し元に多くのオプションを提供することだと思います。

とはいえ、あなたが書いている目の前のコードを、次の人が(合理的な範囲内で)書くコードよりも考慮することが重要だと思います。これは、すでに存在するコードについて推測できるためです。

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

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


0

TL; DR; –要約

  • 社内ソフトウェアを開発するList場合は、コレクションの場合でも、戻り値には特定の型(Likeなど)を使用し、入力パラメーターには最も一般的な型を使用してください。
  • メソッドが再配布可能なライブラリのパブリックAPIの一部である場合は、具象コレクション型の代わりにインターフェースを使用して、戻り値と入力パラメーターの両方を導入してください。
  • メソッドが読み取り専用のコレクションを返す場合は、IReadOnlyListまたはIReadOnlyCollectionを戻り値の型として使用してそれを示します。

もっと

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