次のLINQステートメントはどのように機能しますか?


160

次のLINQステートメントはどのように機能しますか?

これが私のコードです:

var list = new List<int>{1,2,4,5,6};
var even = list.Where(m => m%2 == 0);
list.Add(8);
foreach (var i in even)
{
    Console.WriteLine(i);
}

出力: 2, 4, 6, 8

なんで2, 4, 6


102
クエリ式の結果はクエリであり、クエリの実行ではありません。
Eric Lippert

6
詳細については、この質問に対する承認済みの回答を参照してください。
ダニエル

9
きっとあなたは実際に質問を要約するタイトルを考えることができます。
マットボール

2
反対票(私の意見ではなく6票)についての私の推測では、質問のタイトルが一般的すぎるので、適切な質問ではないと考えています。しかし、賛成票の数を見て、ニュースレターで今週の一番の質問になるので、あまり心配する必要はないと思います。
アベル

回答:


235

出力は遅延実行2,4,6,8原因です。

クエリは、クエリ変数が作成されるときではなく、クエリ変数が反復されるときに実際に実行されます。これは遅延実行と呼ばれます。

-Suprotim Agarwal、「LINQでのクエリ実行の遅延vs即時」

即時クエリ実行と呼ばれる別の実行があり、クエリ結果のキャッシュに役立ちます。Suprotim Agarwalから再び:

シングルトン値を生成しないクエリを即座に実行 するには、クエリまたはクエリ変数でToList(), ToDictionary(), ToArray(), Count(), Average()or Max()メソッドを呼び出します。これらは変換演算子と呼ばれ、結果のコピー/スナップショットを作成でき、クエリを再実行する必要なく、必要な回数だけアクセスできます。

出力をにしたい場合は2,4,6、次を使用します.ToList()

var list = new List<int>{1,2,4,5,6};
var even = list.Where(m => m%2 == 0).ToList();
list.Add(8);
foreach (var i in even)
 {
    Console.WriteLine(i);
 }

8
Count()、Max()、Avg()、Sum()およびおそらくリスト全体を考慮する必要がある他のメソッドも、クエリの評価を引き起こします。
Kenned

1
メソッドとして 'filterList()'ではなく、変数として 'filteredList'を使用することをよく考えました。つまり、メソッドを呼び出すのではなく、リストをフィルター処理するたびに繰り返し処理します。珍しく、おそらくパフォーマンス面で不完全な場合でも、物事を行う方法は興味深いかもしれません。
Katana314 2013

4
@Sebastian -さらにKennedさんのコメント@に、.First().FirstOrDefault().Single().SingleOrDefault()、クエリの評価をトリガーします。
Scotty.NET 2013

4
MC

2
@MCなぜこの質問をしているのかわかりません。一度にすべての回答が得られませんでした。何度か編集されました。
Atish Dipongkor-MVP 2013年

11

これは実行の遅延が原因で発生しました。つまり、式の計算は、どこかで必要になるまで実行されません。これにより、データが大きすぎる場合のパフォーマンスが向上します。


3
これは、高価な列挙が複数回実行されていることを意味することもあるため、微妙な違いがあるかもしれません。このような場合、パフォーマンスが低下する可能性もあります。
絶望のしかめっ面2013

0

この理由は、ラムダ式の遅延実行です。クエリは、foreachループで反復を開始すると実行されます。


11
厳密には、ラムダではなく、イテレータの遅延実行です。
Dスタンリー

0

LINQから取得したIEnumerable <>を使用すると、Enumeratorクラスのみが作成され、反復を使用して初めてウォークが開始されます。


-1

延期された実行のため、この結果が得られます。つまり、結果は最初にアクセスされるまで実際には評価されません。

より明確にするために、スニペットの最後にあるリストに10を追加してからもう一度印刷するだけで、出力に10は表示されません。

     var list = new List<int>{1,2,4,5,6};
    var even = list.Where(m => m%2 == 0).Tolist();
    list.Add(8);
    foreach (var i in even)
    {
        Console.WriteLine(i);
    }
//new*
    list.Add(10);
    foreach (var i in even)
    {
        Console.WriteLine(i);
    }

実際に試してみましたか?10出力を取得します。
Mark Hurd 2013

良いキャッチ@MarkHurd yesは.ToList()を追加しませんでした。投稿を編集したところ、予期した出力が得られるはずです。式が評価されるのは、最初にvarを使用するときにのみ評価されるが、毎回評価されているように見えるのが私の予想でした。
Sandeep

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