Linq: порядок выполнения цепного запроса
Я хочу понять, как обрабатывается цепной запрос. Например, рассмотрим следующий запрос
var sumOfRoots = numbers //IEnum0
.Where(x => x > 0) //IEnum1
.Select(x => Math.Sqrt(x)) //IEnum2
.Select(x => Math.Exp(x)) //IEnum3
.Sum();
Где, например, numbers={-1, 4, 9 }.
Вот что происходит за сценой:
1. Получение всех перечислителей (прямой проход)
-
numbers
вызываетGetEnumerator()
, который возвращает (обозначим его)IEnum0
экземпляр -
IEnum0
вызываетGetEnumerator()
, который возвращаетIEnum1
экземпляр -
IEnum1
вызываетGetEnumerator()
, который возвращаетIEnum2
экземпляр -
IEnum2
вызываетGetEnumerator()
, который возвращаетIEnum3
экземпляр
2. Вызов MoveNext (обратный проход)
-
.Sum()
вызываетMoveNext()
наIEnum3
-
IEnum3
вызываетMoveNext()
наIEnum2
-
IEnum2
вызываетMoveNext()
наIEnum1
-
IEnum1
вызываетMoveNext()
наIEnum0
3. Возврат из MoveNext (прямой-обратный проход)
-
IEnum0
переходит к элементу-1
и возвращаетtrue
. -
IEnum1
проверьте, если-1
удовлетворяют условию (которое не истинно), поэтомуIEnum1
вызываетMoveNext()
наIEnum0
. -
IEnum0
переходит к элементу4
и возвращаетtrue
. -
IEnum1
Проверьте, удовлетворяет ли4
условию (которое истинно) и возвращаетtrue
-
IEnum2
ничего не делает, просто возвращает выводIEnum1
, который являетсяtrue
-
IEnum2
ничего не делает, просто возвращает выводIEnum2
, который являетсяtrue
4. Вызов тока (обратный проход)
-
.Sum()
вызываетCurrent
наIEnum3
. -
IEnum3
вызываетCurrent
наIEnum2
-
IEnum2
вызываетCurrent
наIEnum1
-
IEnum1
вызываетCurrent
наIEnum0
5. Обратный ток (прямой проход)
-
IEnum0
возвращает4
-
IEnum1
возвращает4
-
IEnum2
возвращаетsqrt(4)=2
-
IEnum3
возвращаетexp(2)
6. Повторите шаги 2.-5. до шага 3. возвращает false
Пожалуйста, поправьте меня, если цепной запрос обрабатывается в различный способ.
1 ответ:
Вы можете использовать делегатов для понимания порядка выполнения. Пример:
static void Main(string[] args) { var numbers = new []{ -1, 4, 9 }; double sumOfRoots = numbers.Where(IsGreaterThanZero) .Select(ToSquareRoot) .Select(ToExp) .Sum(x => ToNumber(x)); Console.WriteLine($"sumOfRoots = {sumOfRoots}"); Console.Read(); } private static double ToNumber(double number) { Console.WriteLine($"SumNumber({number})"); return number; } private static double ToSquareRoot(int number) { double value = Math.Sqrt(number); Console.WriteLine($"Math.Sqrt({number}): {value}"); return value; } private static double ToExp(double number) { double value = Math.Exp(number); Console.WriteLine($"Math.Exp({number}): {value}"); return value; } private static bool IsGreaterThanZero(int number) { bool isGreater = number > 0; Console.WriteLine($"{number} > 0: {isGreater}"); return isGreater; }
Вывод:
-1 > 0: False
4 > 0: True
Математика.Sqrt(4): 2
Математика.Exp (2): 7.38905609893065
SumNumber(7.38905609893065)
9 > 0: True
Математика.Sqrt (9): 3
Математика.Exp (3): 20.0855369231877
SumNumber(20.0855369231877)
SumOfRoots = 27,4745930221183