Нечетное лямбда-поведение
Я наткнулся на эту статью и нашел, что это очень интересно, поэтому я провел несколько тестов самостоятельно:
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 имеет только области функций для переменных, поэтому даже ваше второе решение не будет делать то, что вы ожидаете.
Это также очень хорошее объяснение
Ответ 2
@Эрик Липперт подробно объяснил это в двухстраничной статье:
Это обязательная статья, так как она объясняет поведение в глубину и на уровне реализации.
Ответ 3
Да.
В Test One var i
захватывается в цикле, но i
ссылается на переменную, которая эффективно объявляется после внешней петли, поэтому все захваченные лямбды ссылаются на одна переменная. Когда вы вызываете действия, значение i
равно 5
, поэтому весь вывод будет пять.
В Test Two var j
записывается в цикле, но в этом случае j
объявляется каждый раз внутри цикла, поэтому все захваченные лямбды относятся к различные переменные. Поэтому вызов lambdas выводит различные значения.