如何告诉 lambda 函数在 C# 中捕获副本而不是引用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/451779/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to tell a lambda function to capture a copy instead of a reference in C#?
提问by Eclipse
I've been learning C#, and I'm trying to understand lambdas. In this sample below, it prints out 10 ten times.
我一直在学习 C#,我正在尝试理解 lambdas。在下面的这个示例中,它打印了 10 次 10 次。
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();
}
}
Obviously, the generated class behind the lambda is storing a reference or pointer to the int i
variable, and is assigning a new value to the same reference every time the loop iterates. Is there a way to force the lamda to grab a copy instead, like the C++0x syntax
显然,lambda 背后的生成类存储了一个引用或指向int i
变量的指针,并且每次循环迭代时都会为同一个引用分配一个新值。有没有办法强制 lamda 改为获取副本,例如 C++0x 语法
[&](){ ... } // Capture by reference
vs.
对比
[=](){ ... } // Capture copies
采纳答案by Tinister
What the compiler is doing is pulling your lambda and any variables captured by the lambda into a compiler generated nested class.
编译器正在做的是将您的 lambda 和由 lambda 捕获的任何变量拉入编译器生成的嵌套类中。
After compilation your example looks a lot like this:
编译后,您的示例看起来很像这样:
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);
}
}
}
By making a copy within the for loop, the compiler generates new objects in each iteration, like so:
通过在 for 循环中进行复制,编译器会在每次迭代中生成新对象,如下所示:
for (int i = 0; i < 10; ++i)
{
DisplayClass1 displayClass1 = new DisplayClass1();
displayClass1.i = i;
actions.Add(new Action(displayClass1.Lambda));
}
回答by Eclipse
The only solution I've been able to find is to make a local copy first:
我能找到的唯一解决方案是先制作本地副本:
for (int i = 0; i < 10; ++i)
{
int copy = i;
actions.Add(() => Console.WriteLine(copy));
}
But I'm having trouble understanding why putting a copy inside the for-loop is any different than having the lambda capture i
.
但是我无法理解为什么将副本放入 for 循环与使用 lambda capture 有任何不同i
。
回答by JaredPar
The only solution is to make a local copy and reference that within the lambda. All variables in C# (and VB.Net) when accessed in a closure will have reference semantics vs. copy/value semantics. There is no way to change this behavior in either language.
唯一的解决方案是在 lambda 中进行本地复制和引用。在闭包中访问时,C#(和 VB.Net)中的所有变量都将具有引用语义与复制/值语义。两种语言都无法改变这种行为。
Note: It doesn't actually compile as a reference. The compiler hoists the variable into a closure class and redirects accesses of "i" into a field "i" inside the given closure class. It's often easier to think of it as reference semantics though.
注意:它实际上并不编译为参考。编译器将变量提升到闭包类中,并将对“i”的访问重定向到给定闭包类中的字段“i”中。不过,通常更容易将其视为引用语义。
回答by Matt Brunell
Remember that lambda expressions are really only syntactic sugar for anonymous methods.
请记住,lambda 表达式实际上只是匿名方法的语法糖。
That being said, what you are really looking for is how anonymous methods use local variables in a parent scope.
话虽如此,您真正要寻找的是匿名方法如何在父作用域中使用局部变量。
Here's a link describing this. http://www.codeproject.com/KB/cs/InsideAnonymousMethods.aspx#4
这是一个描述这一点的链接。 http://www.codeproject.com/KB/cs/InsideAnonymousMethods.aspx#4