很抱歉,如果这个问题已经被问到,但假设我们有这段代码(我用 Mono 2.10.2 运行它并用 gmcs
2.10.2.0 编译):
using System;
public class App {
public static void Main(string[] args) {
Func<string> f = null;
var strs = new string[]{
"foo",
"bar",
"zar"
};
foreach (var str in strs) {
if ("foo".Equals(str))
f = () => str;
}
Console.WriteLine(f()); // [1]: Prints 'zar'
foreach (var str in strs) {
var localStr = str;
if ("foo".Equals(str))
f = () => localStr;
}
Console.WriteLine(f()); // [2]: Prints 'foo'
{ int i = 0;
for (string str; i < strs.Length; ++i) {
str = strs[i];
if ("foo".Equals(str))
f = () => str;
}}
Console.WriteLine(f()); // [3]: Prints 'zar'
}
}
[1]
似乎合乎逻辑打印与 [3]
相同的内容.但老实说,我不知何故希望它打印出与 [2]
相同的内容。 .我不知何故相信 [1]
的实现会更接近 [2]
.
问题:任何人都可以提供对规范的引用,它确切地说明了 str
是如何产生的。 [1]
中的 lambda 捕获变量(或者甚至迭代器) .
我想我正在寻找的是 foreach
的确切实现循环。
最佳答案
您要求引用规范;相关位置是第 8.8.4 节,其中指出“foreach”循环等效于:
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
embedded-statement
}
请注意,值 v 是在 while 循环之外声明的,因此只有一个循环变量。然后由 lambda 关闭。
更新
因为很多人遇到这个问题,C# 设计和编译器团队更改了 C# 5 以具有这些语义:
while (e.MoveNext()) {
V v = (V)(T)e.Current;
embedded-statement
}
然后就会出现预期的行为——您每次都关闭一个不同的变量。从技术上讲,这是一个重大变化,但希望依赖您所经历的奇怪行为的人数非常少。
请注意,在这方面,C# 2、3 和 4 现在与 C# 5 不兼容。另请注意,更改仅适用于 foreach
,不适用于 for
循环。
参见 http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/了解详情。
评论者 abergmeier 指出:
C# is the only language that has this strange behavior.
此声明绝对错误。考虑以下 JavaScript:
var funcs = [];
var results = [];
for(prop in { a : 10, b : 20 })
{
funcs.push(function() { return prop; });
results.push(funcs[0]());
}
abergmeier,你想猜猜 results
的内容是什么吗?
关于c# - 迭代器的 Lambda 捕获问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7123898/