почему эта программа C # выводит такой результат?Как я понимаю закрытие? - PullRequest
5 голосов
/ 25 марта 2011

Я пытался понять ответ на этот вопрос Почему я получаю неправильные результаты при вызове Func ? Я написал пример кода. Следующий код

public static void Main(string[] args)
{
    var funcs = new List<Func<string>>();
    for(int v=0,i=0;v<3;v++,i++)
    {
        funcs.Add( new Func<string>(delegate(){return "Hello "+ i++ +" "+v;}) );
    }
    foreach(var f in funcs)
        Console.WriteLine(f());
}

производит

Hello 3 3
Hello 4 3
Hello 5 3

Прочитав объяснение Джона Скита и Эрика Липперта, я подумал, что получу

Hello 3 3
Hello 3 3
Hello 3 3

Здесь и v, и i являются переменными цикла, в то время как значение i взято в этот момент v не почему это ?. Я не понимаю поведение.

Ответы [ 4 ]

11 голосов
/ 25 марта 2011

Ну, вы правильно поняли Эрика и Джона, но вы пропустили одну часть своего кода:

"Hello "+ i++ +" "+v;
          ^^^
          this part increments i for each call

Итак, в основном, что-то похожее на это:

  1. Captureанонимная функция 3 раза, захватывая ссылки на переменные в методе, а не в области действия цикла
  2. В конце цикла обе эти переменные имеют значение 3
  3. Выполнитепервая функция, вывод содержимого i и v, а затем увеличение i
  4. выполнение второй функции, вывод содержимого i и v, и поскольку это одно и то же i как и при предыдущем вызове метода, здесь вы получите 4, а не 3
  5. и т. Д.

Если, с другой стороны, вы изменили свой код, захватив переменные внутриобласть видимости цикла, как это:

for(int v=0,i=0;v<3;v++,i++)
{
    int ii = i, vv = v;
    funcs.Add( new Func<string>(delegate(){return "Hello "+ ii++ +" "+vv;}) );
}

Тогда вы получите 0, 0, 1, 1 и 2, 2.Вы по-прежнему увеличиваете переменную ii, вы делаете это после использования захваченного значения в цикле, но затем никогда больше не используете эту переменную (каждый анонимный метод получает свою собственную частную копию.) спасибо @ferosekhanj закомментарии

2 голосов
/ 25 марта 2011

результат правильный (как это не может быть?;)) Когда вы выполняете делегат, после окончания цикла он будет использовать текущее значение переменных i и v.больше не будет меняться, v == 3 в конце цикла.я тоже == 3Но ваш делегат записывает i в вывод, затем увеличивает его (i ++).Поэтому каждый раз, когда выполняется делегат, я буду увеличиваться, но не v.

Это то, что вы наблюдаете.

2 голосов
/ 25 марта 2011

Ответ прост: ++i выполняется внутри вашего делегата, таким образом увеличивая значение каждый раз.Первое значение будет 3, потому что это значение i после цикла.
Поймите, что ваш делегат выполняется не внутри цикла for, а в цикле foreach.

0 голосов
/ 25 марта 2011

Я думаю, что для переменных цикла есть область вне цикла for.

public static void Main(string[] args)
{
    var funcs = new List<Func<string>>();
    int i=0;
    for(int v=0;v<3;v++,i++)
    {
        funcs.Add( new Func<string>(delegate(){return "Hello "+ i++ +" "+v;}) );
    }
    foreach(var f in funcs)
        Console.WriteLine(f());
}

После цикла for i==3 и v==3. Поскольку код не оставляет область действия i между созданием делегатов, все три экземпляра делегата совместно используют один и тот же i. Таким образом, каждый вызов функции будет увеличиваться на один и тот же i, и вы получите 3, 4, 5

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...