如何告诉lambda函数捕获一个副本,而不是在C#中的引用?
我一直在学习C#,并试图理解lambda。 在下面的这个示例中,它打印出十次十次。
class Program { delegate void Action(); static void Main(string[] args) { List<Action> actions = new List<Action>(); for (int i = 0; i < 10; ++i ) actions.Add(()=>Console.WriteLine(i)); foreach (Action a in actions) a(); } }
显然,lambda后面的生成类正在存储对int i
variables的引用或指针,并且每次循环迭代时都将相同的引用分配一个新值。 有没有办法强制lamda抓取一个副本,而不是像C ++ 0x语法
[&](){ ... } // Capture by reference
与
[=](){ ... } // Capture copies
编译器正在做的是将你的lambda和lambda捕获的任何variables拉到编译器生成的嵌套类中。
编译后,你的例子看起来很像这样:
class Program { delegate void Action(); static void Main(string[] args) { List<Action> actions = new List<Action>(); DisplayClass1 displayClass1 = new DisplayClass1(); for (displayClass1.i = 0; displayClass1.i < 10; ++displayClass1.i ) actions.Add(new Action(displayClass1.Lambda)); foreach (Action a in actions) a(); } class DisplayClass1 { int i; void Lambda() { Console.WriteLine(i); } } }
通过在for循环中创build副本,编译器会在每次迭代中生成新的对象,如下所示:
for (int i = 0; i < 10; ++i) { DisplayClass1 displayClass1 = new DisplayClass1(); displayClass1.i = i; actions.Add(new Action(displayClass1.Lambda)); }
唯一的解决办法是在lambda内做一个本地拷贝和引用。 在闭包中访问时,C#(和VB.Net)中的所有variables都将具有引用语义与复制/值语义。 两种语言都无法改变这种行为。
注意:它实际上并不作为参考编译。 编译器将variables提升为闭包类,并在给定的闭包类中将“i”的访问redirect到字段“i”。 尽pipe如此,将其视为引用语义常常更容易。
我能find的唯一解决scheme是先制作本地副本:
for (int i = 0; i < 10; ++i) { int copy = i; actions.Add(() => Console.WriteLine(copy)); }
但是我很难理解为什么在for循环中放置一个副本与使用lambda捕获i
。
请记住,lambdaexpression式实际上只是匿名方法的语法糖。
这就是说,你真正想要的是匿名方法如何在父范围内使用局部variables。
这是描述这个的链接。 http://www.codeproject.com/KB/cs/InsideAnonymousMethods.aspx#4