Это не имеет ничего общего с петлями.
Это поведение вызвано тем, что вы используете лямбда-выражение, () => variable * 2
где внешняя область variable
не определена во внутренней области лямбда-выражения .
Лямбда-выражения (в C # 3 +, а также анонимные методы в C # 2) по-прежнему создают реальные методы. Передача переменных в эти методы сопряжена с некоторыми дилеммами (передача по значению, передача по ссылке, C # идет по ссылке, но это открывает еще одну проблему, когда ссылка может пережить действительную переменную). Что C # делает, чтобы разрешить все эти дилеммы, так это создать новый вспомогательный класс («замыкание») с полями, соответствующими локальным переменным, используемым в лямбда-выражениях, и методами, соответствующими фактическим лямбда-методам. Любые изменения variable
в вашем коде фактически переводятся вClosureClass.variable
Таким образом, ваш цикл while продолжает обновлять ClosureClass.variable
до тех пор, пока он не достигнет 10, тогда цикл for выполняет действия, которые все работают одинаково ClosureClass.variable
.
Чтобы получить ожидаемый результат, вам нужно создать разделение между переменной цикла и закрываемой переменной. Вы можете сделать это, введя другую переменную, а именно:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
var t = variable; // now t will be closured (i.e. replaced by a field in the new class)
actions.Add(() => t * 2);
++variable; // changing variable won't affect the closured variable t
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
Вы также можете переместить замыкание на другой метод для создания этого разделения:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
actions.Add(Mult(variable));
++variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
Вы можете реализовать Mult как лямбда-выражение (неявное закрытие)
static Func<int> Mult(int i)
{
return () => i * 2;
}
или с реальным классом помощника:
public class Helper
{
public int _i;
public Helper(int i)
{
_i = i;
}
public int Method()
{
return _i * 2;
}
}
static Func<int> Mult(int i)
{
Helper help = new Helper(i);
return help.Method;
}
В любом случае, «замыкания» не являются концепцией, связанной с циклами , а скорее с анонимными методами / лямбда-выражениями, использующими локальные переменные в области видимости - хотя некоторые неосторожные использования циклов демонстрируют ловушки замыканий.