在C#中使用lambda表达式或匿名方法时,我们必须警惕对修改后的闭包陷阱的访问.例如:
foreach (var s in strings)
{
query = query.Where(i => i.Prop == s); // access to modified closure
...
}
Run Code Online (Sandbox Code Playgroud)
由于修改后的闭包,上面的代码将导致Where查询中的所有子句都基于最终值s.
正如这里所解释的那样,这是因为上面循环中s声明的变量foreach在编译器中被翻译成这样:
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
...
}
Run Code Online (Sandbox Code Playgroud)
而不是像这样:
while (enumerator.MoveNext())
{
string s;
s = enumerator.Current;
...
}
Run Code Online (Sandbox Code Playgroud)
正如这里所指出的,在循环外声明变量没有性能优势,在正常情况下,我能想到这样做的唯一原因是你计划在循环范围之外使用变量:
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
...
}
var finalString = s;
Run Code Online (Sandbox Code Playgroud)
但是,foreach循环中定义的变量不能在循环外使用: …
string [] files = new string[2];
files[0] = "ThinkFarAhead.Example.Settings.Configuration_Local.xml";
files[1] = "ThinkFarAhead.Example.Settings.Configuration_Global.xml";
//Resharper complains this is an "access to modified closure"
for (int i = 0; i < files.Length; i++ )
{
// Resharper disable AccessToModifiedClosure
if(Array.Exists(Assembly.GetExecutingAssembly().GetManifestResourceNames(),
delegate(string name) { return name.Equals(files[i]); }))
return Assembly.GetExecutingAssembly().GetManifestResourceStream(files[i]);
// ReSharper restore AccessToModifiedClosure
}
Run Code Online (Sandbox Code Playgroud)
虽然ReSharper抱怨这是"访问修改后的闭包",但上述情况似乎工作正常.任何人都可以阐明这一点吗?
(这个主题在这里继续)
鉴于:
void AFunction()
{
foreach(AClass i in AClassCollection)
{
listOfLambdaFunctions.AddLast( () => { PrintLine(i.name); } );
}
}
void Main()
{
AFunction();
foreach( var i in listOfLambdaFunctions)
i();
}
Run Code Online (Sandbox Code Playgroud)
现在你会认为这样做会对等:
void Main()
{
foreach(AClass i in AClassCollection)
PrintLine(i.name);
}
Run Code Online (Sandbox Code Playgroud)
但它没有,它将做的是每次打印AClassCollection中最后一项的名称!所以基本上每个lambda函数都使用相同的项目.我怀疑"当lambda被创建时"或"当它使用其中使用的外部变量的快照时"可能会有一些延迟,或者基本上只是持有'对局部变量的引用'i'
所以我这样做了:
string astr = "a string";
AFunc fnc = () => { System.Diagnostics.Debug.WriteLine(astr); };
astr = "changed";
fnc();
Run Code Online (Sandbox Code Playgroud)
而惊喜,惊喜,它输出"改变了!"
我正在使用XNA 3.1(无论是什么c#)
到底是怎么回事?lambda函数以某种方式存储变量或其他东西的"引用"吗?反正这个问题呢?