Понимание того, как компилятор C# работает с методами LINQ цепочки
Я пытаюсь понять, что делает компилятор C#, когда я связываю методы linq, особенно при цепочке одного и того же метода несколько раз.
простой пример: предположим, я пытаюсь отфильтровать последовательность ints на основе двух условий.
самое очевидное, что нужно сделать, это что-то вроде этого:
IEnumerable<int> Method1(IEnumerable<int> input)
{
return input.Where(i => i % 3 == 0 && i % 5 == 0);
}
а то мог бы также свяжите методы where с одним условием в каждый:
IEnumerable<int> Method2(IEnumerable<int> input)
{
return input.Where(i => i % 3 == 0).Where(i => i % 5 == 0);
}
Я взглянул на IL в отражателе; очевидно, что он отличается для двух методов, но анализ его дальше выходит за рамки моих знаний на данный момент:)
Я хотел бы узнать:
a) что компилятор делает по-разному в каждом экземпляре и почему.
b) есть ли какие-либо последствия для производительности (не пытаясь микро-оптимизировать; просто любопытно!)
2 ответов
ответ (a) короткий, но я более подробно остановлюсь ниже:
компилятор фактически не выполняет цепочку-это происходит во время выполнения, через обычную организацию объектов! Здесь гораздо меньше магии, чем может показаться на первый взгляд-Джон Скит!--15-->недавно завершен шаг" где предложение" в своей серии блогов повторная реализация LINQ для объектов. Я бы рекомендовал прочитать это.
в очень короткие условия, что происходит это: каждый раз, когда вы называете Where
метод расширения, он возвращает новый WhereEnumerable
объект, который имеет две вещи-ссылку на предыдущий IEnumerable
(вы назвали его Where
on), и лямбда, которую вы предоставили.
когда вы начинаете повторять это WhereEnumerable
(например,foreach
позже в коде), внутренне он просто начинает итерацию IEnumerable
это это есть ссылка.
"это
foreach
просто попросил меня о следующем элементе в моей последовательности, поэтому я поворачиваюсь и прошу вас о следующем элементе в код последовательности".
это идет вниз по цепочке, пока мы не попадем в начало координат, которое на самом деле является своего рода массивом или хранилищем реальных элементов. Поскольку каждый перечисляемый затем говорит "ОК, вот мой элемент", передавая его обратно в цепочку, он также применяет свою собственную логику. Для Where
, Он применяет лямбда, чтобы увидеть, передает ли элемент критерий. Если это так, это позволяет ему перейти к следующему абоненту. Если это не удается, он останавливается в этой точке, возвращается к своему Перечисляемому и запрашивает следующий элемент.
это происходит до тех пор, пока все MoveNext
возвращает false, что означает, что перечисление завершено и больше нет элементов.
ответить (b), есть всегда разница, но здесь это слишком тривиально, чтобы беспокоиться. Не беспокойся об этом :)
первый будет использовать один итератор, второй будет использовать два. То есть, сначала устанавливается трубопровод с одним этапом, Второй будет включать в себя два этапа.
два итератора имеют небольшой недостаток производительности для одного.