Нечетное лямбда-поведение

Я наткнулся на эту статью и нашел, что это очень интересно, поэтому я провел несколько тестов самостоятельно:

Test One:

List<Action> actions = new List<Action>();

for (int i = 0; i < 5; ++i)
    actions.Add(() => Console.WriteLine(i));

foreach (Action action in actions)
    action();

Выходы:

5
5
5
5
5

Test Two:

List<Action> actions = new List<Action>();

for (int i = 0; i < 5; ++i)
{
    int j = i;
    actions.Add(() => Console.WriteLine(j));
}

foreach (Action action in actions)
    action();

Выходы:

0
1
2
3
4

Согласно статье, в Test One все лямбды содержат ссылку на i, которая выводит их на все выходные 5. Означает ли это, что я получаю ожидаемые результаты в Test Two, потому что создается новый int для каждое лямбда-выражение?

Ответ 1

Это связано с захватом переменной на С#, которая может быть немного сложной

В двух словах: Каждый цикл цикла for ссылается на ту же переменную i, поэтому компилятор использует одно и то же лямбда-выражение для всех циклов.

Если это какое-то утешение, эта странность хуже в javascript, поскольку javascript имеет только области функций для переменных, поэтому даже ваше второе решение не будет делать то, что вы ожидаете.

Это также очень хорошее объяснение

Ответ 3

Да.

В Test One var i захватывается в цикле, но i ссылается на переменную, которая эффективно объявляется после внешней петли, поэтому все захваченные лямбды ссылаются на одна переменная. Когда вы вызываете действия, значение i равно 5, поэтому весь вывод будет пять.

В Test Two var j записывается в цикле, но в этом случае j объявляется каждый раз внутри цикла, поэтому все захваченные лямбды относятся к различные переменные. Поэтому вызов lambdas выводит различные значения.