如何告诉lambda函数在C#中捕获副本而不是引用?
我一直在学习C#,我正在努力理解lambdas。在下面的示例中,它打印了10次如何告诉lambda函数在C#中捕获副本而不是引用?,c#,loops,lambda,capture,C#,Loops,Lambda,Capture,我一直在学习C#,我正在努力理解lambdas。在下面的示例中,它打印了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
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();
}
}
vs
我能找到的唯一解决方案是首先制作本地副本:
for (int i = 0; i < 10; ++i)
{
int copy = i;
actions.Add(() => Console.WriteLine(copy));
}
for(int i=0;i<10;++i)
{
int copy=i;
actions.Add(()=>Console.WriteLine(复制));
}
但是我很难理解为什么将副本放在for循环中与使用lambda捕获不同。唯一的解决方案是创建一个本地副本并在lambda中引用它。当在闭包中访问C#(和VB.Net)中的所有变量时,它们将具有引用语义而不是复制/值语义。这两种语言都无法改变这种行为
注意:它实际上并不是作为引用编译的。编译器将变量提升到闭包类中,并将对“i”的访问重定向到给定闭包类内的字段“i”。不过,通常更容易将其视为引用语义 记住lambda表达式实际上只是匿名方法的语法糖 也就是说,您真正想要了解的是匿名方法如何在父范围中使用局部变量
这里有一个链接描述了这一点 编译器所做的是将lambda和lambda捕获的任何变量拉入编译器生成的嵌套类中 编译后,您的示例看起来非常像这样:
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);
}
}
}
类程序
{
委托无效操作();
静态void Main(字符串[]参数)
{
列表操作=新建列表();
DisplayClass1 DisplayClass1=新的DisplayClass1();
对于(displayClass1.i=0;displayClass1.i<10;++displayClass1.i)
actions.Add(新操作(displayClass1.Lambda));
foreach(动作中的动作a)
a();
}
类别显示类别1
{
int i;
void Lambda()
{
控制台写入线(i);
}
}
}
通过在for循环中创建副本,编译器在每次迭代中生成新对象,如下所示:
for (int i = 0; i < 10; ++i)
{
DisplayClass1 displayClass1 = new DisplayClass1();
displayClass1.i = i;
actions.Add(new Action(displayClass1.Lambda));
}
for(int i=0;i<10;++i)
{
DisplayClass1 DisplayClass1=新的DisplayClass1();
displayClass1.i=i;
actions.Add(新操作(displayClass1.Lambda));
}
因为int的声明在for循环中,所以每次都会重新创建它。有10个不同的整数,都名为“copy”,其中只有一个整数名为“i”,在这个范围内,你可能想读,由我们自己的Jon Skeet撰写。可能的副本我发现很奇怪,这个问题的大多数答案都解释了捕获语义,这对问题的作者来说是非常清楚的,而只有一些提到了解决方案(临时副本)。没有人在回答问题之前读过问题吗?在我看来,描述这种行为的链接是最好的答案。这对于计算表达式试图计算参数时发生的情况非常有帮助。作者无意冒犯,但这不应该是公认的答案。问题是关于强迫C#通过值捕捉变量,这个答案只解释了机制。我仍然不知道如何通过值进行捕获,除非我使用的是DisplayClass1
而不是lambda(IMO无法实现这一目的)。
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 (int i = 0; i < 10; ++i)
{
DisplayClass1 displayClass1 = new DisplayClass1();
displayClass1.i = i;
actions.Add(new Action(displayClass1.Lambda));
}