Почему рассчитывается порядок методов LINQ to objects

Я читаю этот вопрос, который объясняет, что порядок методов LINQ для объектов имеет значение. Мой вопрос: почему?

Если я пишу запрос LINQ to SQL, не имеет значения порядок методов LINQ - projections, например:

session.Query<Person>().OrderBy(x => x.Id)
                       .Where(x => x.Name == "gdoron")
                       .ToList();

Дерево выражений будет преобразовано в рациональный SQL следующим образом:

  SELECT   * 
  FROM     Persons
  WHERE    Name = 'gdoron'
  ORDER BY Id; 

Когда я запустил запрос, SQL-запрос будет построен в соответствии с деревом выражений независимо от того, насколько странно порядок методов.
Почему это не работает с LINQ to objects?
когда я перечисляю IQueryable, все проекции могут быть размещены в рациональном порядке (например, Order By after Where), как это делает оптимизатор базы данных.

Ответ 1

Почему это не работает с LINQ для объектов?

LINQ to Objects не использует деревья выражений. Оператор напрямую преобразуется в ряд вызовов методов, каждый из которых работает как обычный метод С#.

Таким образом, в LINQ to Objects:

   var results = collection.OrderBy(x => x.Id)
                   .Where(x => x.Name == "gdoron")
                   .ToList();

Возвращается к прямым вызовам метода:

   var results = Enumerable.ToList(
                   Enumerable.Where(
                     Enumerable.OrderBy(collection, x => x.Id),
                     x => x.Name = "gdoron"
                   )
                 );

Изучив вызовы методов, вы можете понять, почему они упорядочивают вопросы. В этом случае, сначала разместив OrderBy, вы эффективно вложите его в вызов самого внутреннего метода. Это означает, что вся коллекция будет упорядочена при перечислении переменных. Если вы должны были изменить порядок:

   var results = collection
                   .Where(x => x.Name == "gdoron")
                   .OrderBy(x => x.Id)
                   .ToList();

Затем результирующая цепочка методов переключается на:

   var results = Enumerable.ToList(
                   Enumerable.OrderBy(
                     Enumerable.Where(collection, x => x.Name = "gdoron"),
                     x => x.Id
                   )
                 );

Это, в свою очередь, означает, что только отфильтрованные результаты нужно будет сортировать по мере выполнения OrderBy.

Ответ 2

Linq для объектов отложенное выполнение работает иначе, чем linq-to-sql (и EF).

С помощью linq-to-objects цепочка методов будет выполняться в том порядке, в котором перечислены методы: она не использует деревья выражений для хранения и перевода всего объекта.

Вызов OrderBy, а затем Where с linq-to-objects будет, когда вы перечисляете результаты, сортируйте коллекцию, , затем отфильтруйте ее. И наоборот, фильтрация результатов с помощью вызова Where перед его сортировкой с помощью OrderBy будет, когда вы перечисляете, сначала фильтруете, а затем сортируете. В результате последний случай может иметь огромное значение, поскольку вы потенциально можете сортировать гораздо меньше элементов.

Ответ 3

Поскольку в LINQ для SQL грамматика SQL для SELECT указывает, что различные предложения встречаются в определенной последовательности. Компилятор должен генерировать грамматически правильный SQL.

Применение LINQ для объектов в IEnumerable предполагает итерацию над IEnumerable и применение последовательности действий к каждому объекту в IEnumerable. Вопросы заказа: некоторые действия могут трансформировать объект (или поток самих объектов), другие могут отбрасывать объекты (или вводить новые объекты в поток).

Компилятор не может объяснить ваши намерения. Он создает код, который делает то, что вы сказали сделать в том порядке, в котором вы сказали, чтобы сделать это.

Ответ 4

Совершенно законно использовать побочные операции. Для сравнения:

"crabapple"
    .OrderBy(c => { Console.Write(c); return c; })
    .Where(c => { Console.Write(c); return c > 'c'; })
    .Count();
"crabapple"
    .Where(c => { Console.Write(c); return c > 'c'; })
    .OrderBy(c => { Console.Write(c); return c; })
    .Count();

Ответ 5

Linq to Objects не переупорядочивает, чтобы избежать потенциального шага выполнения, чтобы сделать что-то, что должно быть оптимизировано во время кодирования. Редакторы мира могут в какой-то момент внедрить инструменты анализа кода, чтобы курить оптимизационные возможности, подобные этому, но это определенно не работа для среды выполнения.