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 6

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