迭代器的 Lambda 捕获问题? [英] Lambda capture problem with iterators?
问题描述
抱歉,如果这个问题已经被问过了,但假设我们有这个代码(我用 Mono 2.10.2 运行它并用 gmcs
2.10.2.0 编译):
Apologies if this question has been asked already, but suppose we have this code (I've run it with Mono 2.10.2 and compiled with 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]
.
It seems logical that [1]
print the same as [3]
. But to be honest, I somehow expected it to print the same as [2]
. I somehow believed the implementation of [1]
would be closer to [2]
.
问题:任何人都可以提供对规范的引用,其中说明 str
变量(甚至迭代器)是如何被 str
变量(甚至迭代器)在 <代码>[1].
Question: Could anyone please provide a reference to the specification where it tells exactly how the str
variable (or perhaps even the iterator) is captured by the lambda in [1]
.
我想我正在寻找的是 foreach
循环的确切实现.
I guess what I am looking for is the exact implementation of the foreach
loop.
推荐答案
您要求参考规范;相关位置是第 8.8.4 节,其中指出foreach"循环等效于:
You asked for a reference to the specification; the relevant location is section 8.8.4, which states that a "foreach" loop is equivalent to:
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
embedded-statement
}
请注意,值 v 是在 while 循环之外声明的,因此只有一个循环变量.然后由 lambda 关闭.
Note that the value v is declared outside the while loop, and therefore there is a single loop variable. That is then closed over by the lambda.
由于遇到这个问题的人太多,C# 设计和编译器团队将 C# 5 更改为具有这些语义:
while (e.MoveNext()) {
V v = (V)(T)e.Current;
embedded-statement
}
然后具有预期的行为 - 您每次关闭不同的变量.从技术上讲,这是一个重大变化,但希望依赖于您所经历的怪异行为的人数非常少.
Which then has the expected behaviour -- you close over a different variable every time. Technically that is a breaking change, but the number of people who depend on the weird behaviour you are experiencing is hopefully very small.
请注意,在这方面,C# 2、3 和 4 现在与 C# 5 不兼容.另请注意,此更改仅适用于 foreach
,而不适用于 for
循环.
Be aware that C# 2, 3, and 4 are now incompatible with C# 5 in this regard. Also note that the change only applies to foreach
, not to for
loops.
参见 http://ericlippert.com/2009/11/12/closure-over-the-loop-variable-thinked-harmful-part-one/ 了解详情.
评论员 abergmeier 说:
Commenter abergmeier states:
C# 是唯一具有这种奇怪行为的语言.
C# is the only language that has this strange behavior.
此声明绝对错误.考虑以下 JavaScript:
This statement is categorically false. Consider the following JavaScript:
var funcs = [];
var results = [];
for(prop in { a : 10, b : 20 })
{
funcs.push(function() { return prop; });
results.push(funcs[0]());
}
abergmeier,你愿意猜猜results
的内容是什么吗?
abergmeier, would you care to take a guess as to what are the contents of results
?
这篇关于迭代器的 Lambda 捕获问题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!