Lambda捕获问题与迭代器?

sin*_*raj 2 c# lambda closures scope

如果已经问过这个问题,请道歉,但假设我们有这个代码(我用Mono 2.10.2运行它并用gmcs2.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'
    }
}
Run Code Online (Sandbox Code Playgroud)

[1]打印相同的逻辑似乎是合乎逻辑的[3].但说实话,我不知何故预计它会打印出来[2].我不知何故相信实施[1]会更接近[2].

问题:任何人都可以提供对规范的引用,它可以准确地告诉strlambda 如何捕获变量(或者甚至是迭代器)[1].

我想我要找的是foreach循环的确切实现.

Eri*_*ert 11

您要求参考规范; 相关位置是第8.8.4节,其中声明"foreach"循环等效于:

    V v;
    while (e.MoveNext()) {
        v = (V)(T)e.Current;
        embedded-statement
    }
Run Code Online (Sandbox Code Playgroud)

请注意,值v在while循环之外声明,因此存在单个循环变量.然后由lambda关闭.

UPDATE

因为有很多人遇到这个问题,C#设计和编译团队改变了C#5以获得这些语义:

    while (e.MoveNext()) {
        V v = (V)(T)e.Current;
        embedded-statement
    }
Run Code Online (Sandbox Code Playgroud)

然后具有预期的行为 - 每次都关闭不同的变量.从技术上讲,这是一个突破性的变化,但是依赖于您正在经历的奇怪行为的人数希望非常小.

请注意,在这方面,C#2,3和4现在与C#5不兼容.另请注意,更改仅适用foreachfor循环,而不适用于循环.

有关详细信息,请参见http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/.


Commenter abergmeier说:

C#是唯一具有这种奇怪行为的语言.

这句话绝对是错误的.考虑以下JavaScript:

var funcs = [];
var results = [];
for(prop in { a : 10, b : 20 })
{
  funcs.push(function() { return prop; });
  results.push(funcs[0]());
}
Run Code Online (Sandbox Code Playgroud)

像是,你愿意猜一下内容是results什么吗?