你可以用委托和lambda表达式解释一个例子
List<Func<int>> list = new List<Func<int>>();
for (int i = 0; i < 10; i++)
{
list.Add(() => i);
}
foreach (var func in list)
{
Console.WriteLine(func());
}
据我所知,我有List的引用方法没有params并返回Int,但为什么它从循环中返回10倍的最大值?这个怎么运作? THX!
答案 0 :(得分:4)
当你这样做时会关闭:
(() => i)
lambda获取原始变量i
,而不是副本,因此你得到10次 - 因为在调用delegate时i
的原始值是10(循环之后)
如果更改代码并添加本地临时变量,则会得到0 1 2 3 4 5 6 7 8 9:
for (int i = 0; i < 10; i++)
{
int j = i;
list.Add(() => j);
}
您可以在此处阅读有关闭包的内容: Closures
答案 1 :(得分:1)
原因是lambda () => i
捕获局部变量i
。这意味着i
在添加到列表中时不会被评估,但是当您实际使用()
调用lambda时。
当您的代码(Console.WriteLine(func());
)发生这种情况时,i
的值已经10
,因为for
循环已经完成。
如果你想避免这种行为,你必须将i
的值复制到一个局部变量中,该变量在创建lambda后不会改变。
for (int i = 0; i < 10; i++)
{
int tmp = i;
list.Add(() => tmp);
}
答案 2 :(得分:1)
当你在委托方法中传递变量时,它是它的链接,而不是它在委托中使用的值。
我们创建功能列表:
List<Func<int>> list = new List<Func<int>>();
这里我们使用函数初始化列表,每个函数都应该使用对内存的引用,其中i
变量在被触发时存储:
for (int i = 0; i < 10; i++)
{
list.Add(() => i);
}
现在是时候触发每个函数了,但此时for
循环已经完成执行并且i
变量保持其最终值为10.请记住,委托总是可以找到参数,因为它保存了引用它。它不能被垃圾收集:
foreach (var func in list)
{
// by the time we do it it has value of 10
Console.WriteLine(func());
}
答案 3 :(得分:0)
实际上lambda表达式是一个委托,你在循环结束后调用它,那时i
的值 10 ,所以当代表被调用时,他们都有i
值 10 因为关闭而使用相同的副本,您需要写为@Roma建议让它按预期工作,否则它会,
这个:
for (int i = 0; i < 10; i++)
{
list.Add(() => i);
}
可以看作:
int i;
for (i=0; i < 10; i++)
{
list.Add(() => i);
}