這是關閉如何工作,他們捕獲變量,而不是值。所以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
被修改。
編輯:
您正確的注意,這不會爲引用類型的工作。在這種情況下,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
相同的字符串的引用。
哇,首先,你一段時間沒有看到你的回答。其次,現在非常清楚,儘管我看着IL,對我來說它有點古怪。我沒有意識到它就是這樣。 – Freeman 2013-03-07 16:04:27
Eric,你說這個行爲可能會在未來的C#版本中發生變化。我知道你和MS說了你的再見。但是,這種改變是否已經達到5.0或者仍然認爲會發生? – 2013-03-07 16:07:55
@ P.Brian.Mackey:lambda捕獲一個外部變量的行爲是通過設計而永遠不會改變的。但是,我們在C#5中做了一些改變:foreach *的*循環變量現在邏輯上*在循環體內,而不是邏輯上*在循環體外部。因此,每當關閉foreach的循環變量時,就會得到一個* fresh *變量來關閉C#5.0中的變量,而不是像在C#4.0中那樣的共享變量。 – 2013-03-07 16:09:48