Является ли выбор с последующим Где в двух итерациях над IEnumerable? - PullRequest
5 голосов
/ 03 ноября 2011

Допустим, у меня есть

IEnumerable<int> list = new int[] { 1, 2, 3 };
List<int> filtered = list.Select(item => item * 10).Where(item => item < 20).ToList();

Вопрос в том, есть ли две итерации или только одна.

Другими словами, это эквивалентно по производительности:

IEnumerable<int> list = new int[] { 1, 2, 3 };
List<int> filtered = new List<int>();
foreach(int item in list) {
    int newItem = item * 10;
    if(newItem < 20)
        filtered.Add(newItem);
}

Ответы [ 2 ]

7 голосов
/ 03 ноября 2011

Существует одна итерация для коллекции, выполняемая при вызове метода .ToArray, поэтому оба должны быть эквивалентны. .Select - это проекция, а .Where - это фильтр, оба выражаются в виде деревьев выражений в исходном наборе данных.

Может быть легко доказано:

public class Foo: IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator()
    {
        yield return 1;
        Console.WriteLine("we are at element 1");
        yield return 2;
        Console.WriteLine("we are at element 2");
        yield return 3;
        Console.WriteLine("we are at element 3");
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

class Program
{
    static void Main()
    {
        var filtered = new Foo()
            .Select(item => item * 10)
            .Where(item => item < 20)
            .ToList();
    }
}

при запуске печатает следующее:

we are at element 1
we are at element 2
we are at element 3
2 голосов
/ 03 ноября 2011

В Linq to Objects WHERE и SELECT не выполняются итерации по перечисляемому. Вызывающий код перечисляет его, когда он выполняет foreach для запроса или ToList или ToArray () и т. Д.

В Linq to SQL нет итерации, как раньше. Когда вы делаете ToList или ToArray (), запрос выполняется базой данных. В зависимости от типа запроса БД может искать индексы или выполнять сканирование таблицы.

...