C#lambda,当你想到的时候没有采用局部变量值?

鉴于:

void AFunction() { foreach(AClass i in AClassCollection) { listOfLambdaFunctions.AddLast( () => { PrintLine(i.name); } ); } } void Main() { AFunction(); foreach( var i in listOfLambdaFunctions) i(); } 

现在你会认为这样做会对等:

 void Main() { foreach(AClass i in AClassCollection) PrintLine(i.name); } 

但事实并非如此,它每次都会打印出AClassCollection中最后一项的名称! 所以基本上每个lambda函数都使用相同的项目。 我怀疑“当lambda被创建时”或“当它使用其中使用的外部变量的快照时”可能有一些延迟,或者基本上,只是持有’对局部变量的引用’i’

所以我这样做了:

 string astr = "a string"; AFunc fnc = () => { System.Diagnostics.Debug.WriteLine(astr); }; astr = "chagnged!"; fnc(); 

而惊喜,惊喜,它输出“改变了!”

我正在使用XNA 3.1(无论是什么c#)

到底是怎么回事? lambda函数以某种方式存储变量或其他东西的“引用”吗? 反正这个问题呢?

这是一个修改过的闭包

请参阅:类似问题,例如Access to Modified Closure

要解决此问题,您必须在for循环的范围内存储变量的副本:

  foreach(AClass i in AClassCollection) { AClass anotherI= i; listOfLambdaFunctions.AddLast( () => { PrintLine(anotherI.name); } ); } 

lambda函数以某种方式存储变量或其他东西的“引用”吗?

关。 lambda函数捕获变量本身。 不需要存储对变量的引用 ,事实上,在.NET中永久存储对变量的引用是不可能的。 您只需捕获整个变量 。 您永远不会捕获变量的

请记住,变量是存储位置。 名称“i”指的是特定的存储位置,在您的情况下,它始终指的是相同的存储位置。

反正这个问题呢?

是。 每次循环创建一个新变量。 然后闭合每次捕获一个不同的变量。

这是C#最常报告的问题之一。 我们正在考虑更改循环变量声明的语义,以便每次通过循环创建一个新变量。

有关此问题的更多详细信息,请参阅有关此主题的文章:

http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/

到底是怎么回事? lambda函数以某种方式存储变量或其他东西的“引用”吗?

是的,是的; c#捕获的变量是变量 ,而不是变量 。 你通常可以通过引入一个临时变量并绑定到它来解决这个问题:

 string astr = "a string"; var tmp = astr; AFunc fnc = () => { System.Diagnostics.Debug.WriteLine(tmp); }; 

特别是在臭名昭着的foreach

是的,lambda存储对变量的引用(概念上讲,无论如何)。

一个非常简单的解决方法是:

  foreach(AClass i in AClassCollection) { AClass j = i; listOfLambdaFunctions.AddLast( () => { PrintLine(j.name); } ); } 

在foreach循环的每次迭代中,都会创建一个新的 j ,lambda捕获该j 。 另一方面, i始终是相同的变量,但每次迭代都会更新(所以所有lambda都会看到最后一个值)

我同意这有点令人惊讶。 🙂

我也被这个人抓住了,正如卡尔加里·科德所说的那样,这是一个改进的封闭。 在我拿到resharper之前我真的很难发现它们。 因为它是resharper监视的警告之一,所以我更好地识别它们,因为我编码。