Lambda capture problem with iterators?

后端 未结 4 403
自闭症患者
自闭症患者 2020-12-20 04:00

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):



        
4条回答
  •  轮回少年
    2020-12-20 04:24

    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
        }
    

    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.

    UPDATE

    Because so many people run into this problem the C# design and compiler team changed C# 5 to have these semantics:

        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.

    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.

    See http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/ for details.


    Commenter abergmeier states:

    C# is the only language that has this strange behavior.

    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, would you care to take a guess as to what are the contents of results?

提交回复
热议问题