32

初めて .NET で Entity Framework を使用し、モデルから情報を取得するために LINQ クエリを作成しました。最初から良い習慣でプログラミングしたいと思っているので、これらのクエリをどのように記述して結果を得るのが最善かを研究しています。残念ながら、Stack Exchange を閲覧していると、遅延/即時実行が LINQ でどのように機能するかについて、矛盾する 2 つの説明に出くわしたようです。

  • foreach により、ループの各反復でクエリが実行されます。

問題のデモンストレーションLINQ クエリで foreach() が遅い - ToList() はパフォーマンスを大幅に向上させます - これはなぜですか? これは、foreach がデータ ソースのクエリを繰り返し評価しているため、クエリをすぐに評価するために "ToList()" を呼び出す必要があることを意味し、操作が大幅に遅くなります。

もう 1 つの例は、グループ化されたlinq の結果による Foreaching が信じられないほど遅いという質問です。ヒントはありますか? 、受け入れられた回答は、クエリで「ToList()」を呼び出すとパフォーマンスが向上することも意味します。

  • foreach はクエリを 1 回実行するため、LINQ で安全に使用できます。

問題のデモンストレーションforeach はクエリを 1 回だけ実行しますか? の場合、foreach によって 1 つの列挙が確立され、毎回データソースにクエリが実行されないことを意味します。

サイトのブラウジングを続けると、「foreach ループ中の繰り返し実行」がパフォーマンスの問題の原因であるという多くの質問と、foreach がデータソースから単一のクエリを適切に取得することを示す他の多くの回答が見つかりました。説明には妥当性があるようです。「ToList()」仮説が正しくない場合 (2013 年 6 月 5 日午後 1 時 51 分 EST 時点での現在の回答のほとんどが暗示しているようです)、この誤解はどこから来るのでしょうか? これらの説明のうち、正確なものとそうでないものがありますか?それとも、LINQ クエリの評価が異なる原因となるさまざまな状況がありますか?

編集:以下の受け入れられた回答に加えて、プログラマーに関する次の質問を見つけました。これは、クエリの実行、特にループ中に複数のデータソースにヒットする可能性のある落とし穴を理解するのに非常に役立ちました。この質問に興味のある他の人に役立つ: https://softwareengineering.stackexchange.com/questions/178218/for-vs-foreach-vs-linq

4

8 に答える 8

20

一般に、LINQ は遅延実行を使用します。のようなメソッドを使用するFirst()FirstOrDefault()、クエリはすぐに実行されます。あなたが何かをするとき;

foreach(string s in MyObjects.Select(x => x.AStringProp))

結果はストリーミング方式で、つまり 1 つずつ取得されます。MoveNextイテレータがプロジェクションを呼び出すたびに、次のオブジェクトに適用されます。Where最初にフィルターを適用し、次に投影を適用する場合。

次のようなことをすると;

List<string> names = People.Select(x => x.Name).ToList();
foreach (string name in names)

それなら、これは無駄な操作だと思います。ToList()クエリを強制的に実行し、リストを列挙して射影Peopleを適用します。x => x.Nameその後、リストを再度列挙します。したがって、(IEnumerale ではなく) リストにデータを格納する正当な理由がない限り、CPU サイクルを浪費しているだけです。

一般的に言えば、foreach で列挙しているコレクションに対して LINQ クエリを使用しても、他の同様の実用的なオプションよりもパフォーマンスが低下することはありません。

また、LINQ プロバイダーを実装する人は、Microsoft が提供するプロバイダーと同じように共通のメソッドを機能させることが推奨されますが、必須ではありません。LINQ to HTML または LINQ to My Proprietary Data Format プロバイダーを作成する場合、この方法で動作するという保証はありません。おそらく、データの性質上、即時実行が唯一の現実的なオプションになるでしょう。

また、最終編集。これに興味があるなら、Jon Skeet の C# In Depth は非常に有益であり、非常に読みやすいものです。私の回答は、本の数ページを要約したものです (できれば妥当な精度で) が、LINQ が内部でどのように機能するかについての詳細が必要な場合は、参照するのに適した場所です。

于 2013-06-05T17:35:37.797 に答える
1

違いは、基になる型にあります。LINQ は IEnumerable (または IQueryable) の上に構築されているため、同じ LINQ 演算子のパフォーマンス特性がまったく異なる場合があります。

リストは常に迅速に対応しますが、リストを作成するには事前の努力が必要です。

イテレータも IEnumerable であり、「次の」アイテムを取得するたびに任意のアルゴリズムを使用できます。アイテムの完全なセットを実際に処理する必要がない場合、これはより高速になります。

IEnumerable に対して ToList() を呼び出し、結果のリストをローカル変数に格納することで、任意の IEnumerable をリストに変換できます。これは、次の場合に推奨されます。

  • 遅延実行に依存しません。
  • セット全体よりも多くのアイテムにアクセスする必要があります。
  • すべてのアイテムを取得および保管するための初期費用を支払うことができます。
于 2013-06-05T17:44:17.123 に答える
0

エンティティがなくてもLINQを使用すると、遅延実行が有効になります。実際のlinq式が評価されるのは、反復を強制することによってのみです。その意味で、linq 式を使用するたびに評価されます。

エンティティの場合もこれは同じですが、ここで機能する機能が増えただけです。エンティティ フレームワークが初めて式を確認すると、このクエリが既に実行されているかどうかが確認されます。そうでない場合は、データベースにアクセスしてデータを取得し、内部メモリ モデルをセットアップしてデータを返します。エンティティ フレームワークは、事前にデータをフェッチしたことを認識した場合、データベースにアクセスして、以前にセットアップしたメモリ モデルを使用してデータを返すことはありません。

これにより、生活が楽になりますが、苦痛になることもあります。たとえば、linq 式を使用してテーブルからすべてのレコードを要求するとします。エンティティ フレームワークは、テーブルからすべてのデータを読み込みます。後で同じ linq 式を評価すると、その間にレコードが削除または追加されたとしても、同じ結果が得られます。

エンティティ フレームワークは複雑です。もちろん、独自のメモリ モデルなどの変更を考慮して、クエリを再実行させる方法はあります。

Julia Lerman の「プログラミング エンティティ フレームワーク」を読むことをお勧めします。あなたが今抱えている問題のような多くの問題に対処します。

于 2013-06-05T17:34:12.933 に答える