Как замыкания различаются между foreach и list.ForEach ()? - PullRequest
5 голосов
/ 20 августа 2011

Рассмотрим этот код.

        var values = new List<int> {123, 432, 768};

        var funcs = new List<Func<int>>();

        values.ForEach(v=>funcs.Add(()=>v));

        funcs.ForEach(f=>Console.WriteLine(f()));//prints 123,432,768

        funcs.Clear();

        foreach (var v1 in values)
        {
            funcs.Add(()=>v1);
        }

        foreach (var func in funcs)
        {
            Console.WriteLine(func());  //prints 768,768,768
        } 

Я знаю, что второй foreach печатается 768 3 раза из-за переменной закрытия, захваченной лямбда-кодом.почему это не происходит в первом случае? Чем ключевое слово foreach отличается от метода Foreach?Это потому, что выражение оценивается, когда я values.ForEach

Ответы [ 2 ]

9 голосов
/ 20 августа 2011

foreach вводит только одну переменную. Хотя переменная лямбда-параметра является «новой» каждый раз, когда она вызывается.

Сравнить с:

foreach (var v1 in values) // v1 *same* variable each loop, value changed
{
   var freshV1 = v1; // freshV1 is *new* variable each loop
   funcs.Add(() => freshV1);
} 

foreach (var func in funcs)
{
   Console.WriteLine(func()); //prints 123,432,768
}

То есть

foreach (T v in ...) { }

можно рассматривать как:

T v;
foreach(v in ...) {}

Удачного кодирования.

6 голосов
/ 20 августа 2011

Разница в том, что в цикле foreach у вас есть одиночная переменная v1, которая фиксируется. Эта переменная принимает каждое значение в пределах values - но вы только используете в конце ... это означает, что мы видим только окончательное значение каждый раз.

В вашей версии List<T>.ForEach каждая итерация вводит новую переменную (параметр f), поэтому каждое лямбда-выражение захватывает отдельную переменную, значение которой никогда не меняется.

Эрик Липперт написал об этом в блоге - но учтите, что это поведение может измениться в будущих версиях C #.

...