Понимание основного принципа LINQ Where/Take

У меня есть List<String> myList.

Я хочу взять первые 10 элементов в этом списке, которые соответствуют некоторым критериям (скажем, .Contains("a"), например).

У меня есть:

Var results = myList.Where(o=>o.Contains("a")).Take(10);

Что работает нормально, но выполнил ли LINQ Where, чтобы получить все элементы, соответствующие этому критерию, и затем взял только первые 10 из них? Или это будет скомпилировано таким образом, что будет учитываться весь оператор LINQ (т. е. он будет выполнять Where, но только до тех пор, пока не достигнет 10 элементов)?


person Community    schedule 24.10.2013    source источник


Ответы (3)


LINQ использует ленивую оценку. Когда вы делаете следующую строку:

var results = myList.Where(o=>o.Contains("a")).Take(10);

Ничего не произошло. Только запрос строится. Когда вы перечисляете results (например, с foreach или ToList()), тогда Where и Take будут применяться к myList: Where будет выполняться по мере необходимости, пока не будет найдено до 10 значений true.

person Tim S.    schedule 24.10.2013

Оператор Where возвращает IEnumerable, ожидающий перечисления. Выполнение логики Where откладывается до тех пор, пока вы не запросите у IEnumerable следующее значение.

Оператор Take(10) делает именно это — запрашивает его «следующее совпадение» для условия Where. Это будет выполнено 10 раз, а затем завершится. Но, конечно, по той же причине логика оператора Take(10) на самом деле не выполняется, пока вы не перечислите окончательное возвращаемое значение (результаты в вашем случае).

Так что да, это как бы оптимизировано, но, вероятно, не совсем так, как вы себе представляли.

person Baldrick    schedule 24.10.2013

Посмотрите на код ниже:

using System;
using System.Collections.Generic;
using System.Linq;

public class Test {
    public static void Main(String[] args) {
        var l = new List<String>() { "a", "ab", "b", "bb", "bc", "ba", "c", "ca", "cc" };

        foreach (var s in l.Where(e => { Console.WriteLine(e); return e.Contains("a"); }).Take(3)) {
            Console.WriteLine("loop: {0}", s);
        }
    }
}

Результат:

a
loop: a
ab
loop: ab
b
bb
bc
ba
loop: ba

Итак, как вы можете видеть, он оптимизирован (строки после «c» не оцениваются).

person Sebastian    schedule 24.10.2013