Как работают затворы, они захватывают переменные, а не значения. Таким образом, j
будет изменен.
Если вы не хотите этого, вы можете сделать это:
static void Main(string[] args)
{
int j = 0;
Func<int> f =() =>
{
int k = j;
for (int i = 0; i < 3; i++)
{
k += i;
}
return k;
};
int myStr = f();
Console.WriteLine(myStr);
Console.WriteLine(j);
Console.Read();
}
j
еще захвачен закрытия, но не модифицируется. Изменяется только копия k
.
Edit:
Вы правильно отметить, что это не будет работать для ссылочных типов. В этом случае k = j
хранит копию ссылки на объект. Есть еще одна копия объекта , на которую ссылаются, поэтому любые изменения этого объекта будут влиять на обе переменные.
Вот пример того, как вы будете использовать закрытие для ссылочного типа, а не обновлять исходные переменный:
static void Main(string[] args)
{
Foo j = new Foo(0);
Func<Foo> f =() =>
{
Foo k = new Foo(j.N); // Can't just say k = j;
for (int i = 0; i < 3; i++)
{
k.N += 1;
}
return k;
};
Console.WriteLine(f().N);
Console.WriteLine(j.N);
Console.Read();
}
public class Foo
{
public int N { get; set; }
public Foo(int n) { N = n; }
}
Однако строки будучи неизменных ссылочных типов, вы на самом деле можешь просто сказать k = j
, в отличие от произвольных ссылочных типов. Один из способов думать о неизменности заключается в том, что каждый раз, когда вы обновляете значение строки, вы фактически создаете новый экземпляр. Итак, k = k + "1"
- это как сказать k = new String(k + "1")
. В этот момент он больше не является ссылкой на ту же строку, что и j
.
Ничего себе, прежде всего, не видели ответа от вас какое-то время. Во-вторых, теперь он очень ясен, хотя я смотрел на ИЛ, его несколько безвольный для меня. У меня не было никакой идеи, как это действует. – Freeman
Эрик, вы сказали, что это поведение может измениться в будущей версии C#. Я знаю, что ты и МС сказали, что ты прощай. Однако изменилось ли это изменение на 5.0 или все еще думает, что это произойдет? –
@ P.Brian.Mackey: поведение, которое лямбда захватывает внешнюю переменную, по дизайну и никогда не изменится. Однако мы вносили изменения в C# 5: переменная цикла * foreach * теперь логически * внутри * тела цикла, а не логически * вне * тела цикла. Поэтому каждый раз, когда вы закрываете переменную цикла foreach, вы получаете переменную * fresh * для закрытия в C# 5.0, а не как общую переменную, как в C# 4.0. –