Почему этот кусок кода возвращает 109? - PullRequest
1 голос
/ 12 февраля 2012

Может кто-нибудь сказать мне, как это работает? Буду признателен. Не знаю, как это отладить.

using System;

    class Prg
    {
        private static Func<double, Func<double, double>> Add(int c)
        {
            return x => y => x + y++ + (c *= 2);
        }

        public static void Main(string[] args)
        {
            int a = 1;
            int b = 100;
            var F = Add(2);
            var G = F(a);
            G(b);
            Console.WriteLine(G(b));
        }
    }

РЕДАКТИРОВАТЬ: У меня есть еще один, если вы хотите насладиться нашим экзаменом C # .. Вот код.

delegate int F(); 
class Prg { int a = 10; 
  public F Adder(int x) { 
    int i = x; 
    return delegate { 
      return a += i++; }; 
  } 
  static void Main() { 
    Prg p = new Prg(); 
    F f = p.Adder(5); 
    p.Adder(10); 
    f(); 
    System.Console.Write(f()); 
}  } 

Ответы [ 2 ]

3 голосов
/ 12 февраля 2012

Начну с: никогда не пиши такого кода.

А если вы действительно хотите знать, что происходит:

  1. в строке var F = Add(2), метод Add () создает и возвращает лямбда-выражение с c == 2. Здесь важно то, что c перехватывается лямбда (вы можете прочитать о захвате переменных на msdn .
  2. В строке var G = F(x) вы просто вызываете свою функцию с параметром a = 1 и в результате получаете другую функцию, double -> double, если быть точным.
  3. В строке G(b) вы вызываете свою функцию, именно эту: x + y++ + (c *= 2); Теперь:
    • х равен 1
    • y - ваш параметр, он равен 100
    • c равно 2, но оно захвачено, так что это фактически ссылка на какое-то выделенное поле
    • Результат этого выражения равен 105 (100 ++ возвращает 100, 2 * = 2 возвращает 4), так что вы получите 1 + 100 + 4 = 105. Но, что более важно, переменная c, которая захватывает, теперь равно 4. y был просто параметром, поэтому он не изменился.
  4. В строке Console.... вы снова вызываете свою функцию. На этот раз параметр снова равен 100, поэтому результат будет идентичным, но ваша захваченная переменная c теперь равна 4, поэтому результат вы получите 1 + (100+) + (4 *= 2) -> 1 + 100 + 8 -> 109

Вот краткий пример, который может пролить некоторый свет на то, что там произошло:

class Prg
{
    public static void Main(string[] args)
    {
        int captured = 5;
        int param = 3;

        var func = new Func<int, int>(x => x * captured);

        Console.WriteLine(func(param));
        captured = 6;
        Console.WriteLine(func(param));

    }
}
2 голосов
/ 12 февраля 2012

Эта часть выполняется дважды:

c *= 2

Первый раз, когда вы вызываете G (b).Второй раз, когда вы печатаете на консоль.

Одно выполнение фактически вычисляет:

a + b++ + (c *= 2)

Но так как c умножается на 2 (и присваивается обратно c) при каждом выполнении, вы получаете 109вместо 105. Переменная c упоминается как одна точная ячейка памяти в области действия функции Add, фактически используемая как делегат Func (и экземпляр Func остается неизменным все время), поэтомуне то же самое, что если бы вы делали некоторые вложенные вызовы с функциями (когда в функцию происходила бы только передача по значению, возвращалось бы значение, область действия функции закрывалась и никогда не получала доступ снова с тем же контекстом).

...