迭代器的 Lambda 捕获问题? [英] Lambda capture problem with iterators?

查看:20
本文介绍了迭代器的 Lambda 捕获问题?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

抱歉,如果这个问题已经被问过了,但假设我们有这个代码(我用 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆