IEnumerable <T>ではなくList <T>を使用する必要があるのはなぜですか?


24

ASP.net MVC4 Webアプリケーションでは、IEnumerablesを使用して、実装ではなくインターフェイスにプログラムするというマントラに従っています。

Return IEnumerable(Of Student)

Return New List(Of Student)

リストはクエリを強制的に実行し、IEumerableは実行しないため、人々はIEnumerableではなくListを使用するように言っています。

これは本当にベストプラクティスですか?代替手段はありますか?インターフェイスを使用できる具体的なオブジェクトを使用すると、奇妙に感じます。私の奇妙な気持ちは正当化されますか?


2
えー、まず、なぜクエリを強制的に実行するのが良いのですか?次に、データベース呼び出しを監視およびプロファイリングして、この技術的な考慮事項にメリットがあるかどうかを評価する必要があります。
user16764

2
モデルが完成し、ビューの準備が整ってロードされるように、すべてのクエリを実行する必要があると言われています。つまり、ビューはすべてを受け取り、データベースを照会するべきではありません。
ローワンフリーマン

3
このStackOverflowの質問は、かなりよくカバーしています。
カールビーレフェルト

それは良い答えですが、MVCとの関連性を知りたいです。ビューにIEnumerablesを与えて、すべてのクエリをジャストインタイムで実行できないのはなぜですか?
ローワンフリーマン

2
「すべてのクエリを実行して、モデルが完了し、ビューの準備ができるようにロードする必要があると言われています。つまり、ビューはデータベースをクエリするのではなく、すべてを受信する必要があります。」それはナンセンスです。IEnumerableを渡すと、ビューはデータベースを照会しているかどうかを知りません。そして、それはあるべき姿です。
-user16764

回答:


21

ToList()linqクエリでを実行することが重要な場合があります。これは、クエリをその時点で、期待する順序で実行するためです。ただし、これらのシナリオはまれであり、実際に実行されるまで心配する必要はありません。

簡単に言えIEnumerableば、反復のみが必要なときはいつでも使用しIList、直接インデックス付けする必要があるときに使用し、動的にサイズ調整された配列が必要です(固定サイズの配列でインデックス付けが必要な場合は、標準配列を使用します)。

実行時については、常にIEnumerable変数としてリストを使用できます。そのため、を実行IEnumerableしてを返す.ToList();か、またはをIEnumerable実行.ToList()してパラメーターを渡し、その場で実行IEnumerableを強制することができます。実行を強制するときはいつでも、実行したばかり.ToList()IEnumerable変数に固執して再度実行しないように注意してください。そうしないと、LINQクエリの繰り返しが不必要に2倍になってしまいます。

MVCに関しては、ここで特に注意することはありません。残りの.NETと同じ実行時間ルールに従います。過去の遅延実行セマンティクスに起因する混乱に少し噛まれ、MVCでこれが何らかの関係があると非難した人がいると思いますが、ありません。遅延実行のセマンティクスは、最初はすべての人を混乱させます(その後しばらくの間さえ、少々扱いにくい場合があります)。繰り返しになりますが、LINQクエリが2回実行されないことや、他のコードに対して特定の順序で実行する必要があることを本当に気にするまで心配しないでください。その時点で変数を自分自身に割り当てます。実行を強制すれば大丈夫です。


ビューにIEnumerablesを与えるのは悪いですか?ビューが取得するまでにクエリが実行されるように、リストを指定する必要がありますか?
ローワンフリーマン

@RowanFreemanこれに答えるための編集を追加しました。完全に理解していないものに出くわした人がいると思う(彼らを責めることはできず、実行の遅延は非常に複雑で混乱を招く)セマンティクス。
ジミー・ホッファ

いい答えだ。だから、実際に.ToList()を使用する必要がありますか?これまでのところ、私のアプリケーションは、IEnumerablesのみを使用して、モデルからビューに渡すことで正常に動作します。リストなしまたは.ToList()。私の質問は機能の1つではありません-私のアプリケーションが機能することは知っています。私の質問はベストプラクティスの一つです。
ローワンフリーマン

4
@RowanFreemanのベストプラクティスは、要件を満たす最小限のインターフェイスを使用することです。IEnumerableは現在これを行っているので、変更する心配はありません。とはいえ、クエリが3回または10回実行され、理由がわからない日が来る、またはクエリが挿入の前に実行されることを期待して後で実行されることを期待する日が来るでしょう。これらはあなたが認識する必要がある時間です()は必要に応じて実行を強制し、LINQクエリからIEnumerableを複数回繰り返すと、クエリ全体が複数回実行されます。これらのイベントが発生したとき、あなたの実行を修正
ジミー・ホッファ

1
-をList渡すListことも、リストの内容が変更されることを意味します。コレクションを返す場合は、を使用しますIReadOnlyCollectionListメソッド内で使用し、リストを変更するメソッド間で交換します。それでおしまい!
エリック

7

2つの問題があります。

IENumerable<Data> query = MyQuery();

//Later
foreach (Data item in query) {
  //Process data
}

「プロセスデータ」ループに到達するまでに、クエリは有効でなくなる可能性があります。たとえば、既に破棄されたDataContextでクエリが実行されている場合、コードは例外をスローします。クエリを作成した場所とは異なるコンテキストでクエリを処理している場合、この種のことは非常に複雑になります。

2番目の問題は、「プロセスデータ」ループが完了するまで接続が解放されないことです。これは、「プロセスデータ」が複雑な場合にのみ問題になります。これはhttp://msdn.microsoft.com/en-us/library/bb386929.aspxで言及されています:

Q.データベース接続はいつまで開いていますか?

A.通常、接続はクエリ結果を使用するまで開いたままです。すべての結果を処理するのに時間がかかり、結果をキャッシュすることに反対しない場合は、クエリにToListを適用します。各オブジェクトが1回だけ処理される一般的なシナリオでは、ストリーミングモデルはDataReaderとLINQ to SQLの両方で優れています。

そのため、これらの問題が原因で、たとえばを呼び出すなどして、クエリが実際に実行されるように奨励されていますToList()。ただし、ジミーが示唆するように、リストをIEnumerableとして返すことを妨げるものは何もありません。

一般的なルールとして、IEnumerableの繰り返しを2回以上避けることをお勧めします。コードのコンシューマーがこのルールに従うと仮定すると、クエリを2回実行することで誰かがデータベースに2回ヒットする可能性があるとは考えていません。


1

IEnumerable早期を列挙するもう1つの利点は、適切な場所で例外がスローされることです。これはデバッグを支援します。

たとえば、Razorビューの1つでデッドロック例外が発生した場合、データアクセスメソッドの1つで例外が発生した場合ほど明確ではありません。


IEnumerableスローする可能性のあるを返すメソッドは、おそらく間違いを犯しています。遅延IEnumerableメソッドは2つに分割する必要があります。1つの非遅延メソッドはパラメーターをチェックして設定し、必要に応じてスローします(たとえば、引数がnullのため)。次に、プライベート実装への呼び出しを返しますが、これ延期されます。私は私のコメントは、あなたの答えと対立し、完全にされているとは思わないが、あなたはあなたの答えで重要な側面、アウト残してきたということだと思います使用しての意味論的な意味をIEnumerable対A List(変異)対IReadOnlyCollection(してもメリット延期)
エリック
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.