c# - 迭代器的 Lambda 捕获问题?

标签 c# lambda scope closures

很抱歉,如果这个问题已经被问到,但假设我们有这段代码(我用 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/

相关文章:

c# - 为什么在 .NET 中转换比反射快?

c# - 未命中 IEqualityComparer + GroupBy 的 GetHashCode 中的断点

javascript - 即使用户导航到另一个页面,是否可以让 `XMLHttpRequest` 对象保持事件状态?

c++ - 为什么这个指针在其对象应该超出范围时有效? (以及其他一些奇怪的行为)

c# - 无需办公室即可从 C# 创建 Excel 文件

python - 从 pandas DataFrame 中的日期时间列中提取月份

c++ - 将 std::function 与 lambda 和可变参数模板一起使用

c# - Func<> 与委托(delegate)和 lambda 表达式之间的区别

javascript - 跨多个文件的 Javascript 中的全局变量

c# - 以 Windows 形式播放 youtube 视频