RCode

a software development blog by Bojan Resnik

  • Archives

C# lambda and foreach variable

Posted by Bojan Resnik on June 17, 2009

Can you guess what the output of the following program is?

using System;
using System.Collections.Generic;

namespace Lambda
{
    class Program
    {
        static void Main(string[] args)
        {
            var strings = new[] {"a", "b", "c"};
            var actions = CreateActions(strings);
            actions.ForEach(f => f());
        }
        
        private static List<Action> CreateActions(IEnumerable<string> strings)
        {
            var actions = new List<Action>();
            
            foreach (var s in strings)
                actions.Add( () => Console.WriteLine(s) );
            
            return actions;
        }
    }
}

I expected to see all three strings (a, b and c), but instead I got this:
First output

While analyzing the IL code with Reflector I learned a thing or two about lambdas in C#.
When a lambda is encountered by the compiler, it generates a class which has a field for each local variable used by the lambda. In this case, the generated class would look like this in C#:

private sealed class <>c_DisplayClass3
{
    public string s;
    public void <Main>b_0()
    { Console.WriteLine(this.s); }
}

Of course, the above is not valid C# due to invalid characters in identifiers, but is pretty much what is produced in the IL.
So far so good – no real surprises here. However, look at this code which is my translation from IL to C# of the CreateActions method:

private static List<Action> CreateActions(IEnumerable<string> strings)
{
    List<Action> actions = new List<Action>();
    using (IEnumerator<string> enumerator = strings.GetEnumerator())
    {
        // Note 1: The lambda is created outside of the loop
        <>c_DisplayClass3 lambda = new <>c_DisplayClass3();

        string s;
        while (enumerator.MoveNext())
        {
            s = enumerator.Current;

            // Note 2: The instance field is reassigned each time
            lambda.s = s;
            actions.Add(lambda);
        }
    }
    return actions;
}

The reason for the strange output is clear now – the compiler did not create a lambda object in each iteration but rather reused the existing one each time. After the loop, all items in the actions list are actually the same instance and their s field is the last element of the collection. Apparently, C# compiler creates a lambda instance immediately before the local variable it uses. So, in order to force it to create a new lambda for each iteration, we need to introduce a new local variable in the loop:

private static List<Action> CreateActions(IEnumerable<string> strings)
{
    var actions = new List<Action>();
    
    foreach (var s in strings)
    {
        // This variable will be used in the lambda
        var lambda_param = s;
        actions.Add( () => Console.WriteLine(lambda_param) );
    }
    
    return actions;
}

With this modification the program now produces this output:
Second output

kick it on DotNetKicks.comShout it

Advertisement

41 Responses to “C# lambda and foreach variable”

  1. Flash said

    Great post!

  2. Tormod said

    Nice and precise. Informative. Not only regarding Lambdas, but also how the transparency offered by the occasional C# to IL to C# dive is an essential skill that at least should be somewhere in the development team.

    • Tormod said

      After looking a bit more, I realize that the loop returns exactly what is expected. A lambda performed on an “s” reference, which changes before the lambda is actually executed, should not behave any differently.

  3. C# lambda and foreach variable…

    DotNetBurner – burning hot .net content…

  4. That is really odd. Can anyone explain WHY it works this way? That doesn’t seem intuitive or easily recognizable.

    • Jeff Becker said

      Because a closure captures the variable, not the value.

    • Bojan Resnik said

      Before exploring this, I expected the compiler to create a new lambda object each time it encouters a lambda definition. However, I guess that for the typical usage of lambdas this would be just a waste of time and space.

  5. […] All this time though, I knew that there would be some very interesting translations and manipulations that occurred, but I didn’t really consider that the code which reflector spit out might not actually execute in the same way as the code that I was compiling. Well, today I found just that case (in the latest version of Reflector) while checking out this blog post. […]

  6. […] C# lambda and foreach variable  […]

  7. Thorn said

    As usual, M$ “tricks” leads to ugly code style, named ‘Microsoft experience’.
    Who cares your damn ‘effectiveness’ when you produce really wrong code? As M$ promises, “JIT” must eliminate all bottlenecks, giving optimized, native code. So why you make your own optimizations?

  8. […] C# Lambda and foreach variable Posted by Dana Filed in Uncategorized Tags: c#, lambda, Microsoft MVC, PredicateBuilder Leave a Comment » […]

  9. MKPFS said

    Yeah, that’s why in Java anonymous methods can only access local variables if they are final.

    BTW putting lambdas into procedural/OOP languages makes me sick.

  10. Ecko said

    thanks, great post

  11. Ismail Karolia said

    Wow. Great post.

    I experienced the exact same problem and couldn’t figure out what the problem was.

    Thanks alot.

  12. FYI: Eric Lippert says this behavior will change in C# 5: http://stackoverflow.com/questions/8898925/is-there-a-reason-for-cs-reuse-of-the-variable-in-a-foreach/8899347#8899347

  13. nicdex said

    the javascript closure works the same way. you need to declare a local var to have the right value. maybe it’s why they implemented it this way in c#.

  14. chambem9 said

    The reason that it was originally implemented that way is because in C#1 there was no concept of closures, so it made no difference whether the variable was declared inside or outside the loop. (I guess they decided to use the outer scope for efficiency.) When closures were implemented, this behaviour was then inherited for reasons of backwards compatibility. As Eric Lippert points out on his blog (http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx), this was something they always wanted to change, and he feels it’s a shame that it’s taken so long to do so.

  15. Dev said

    interesting post..’yield’ would directly give the desired result.
    IEnumerable CreateActions(string[] str)
    {
    foreach (var item in str)
    {
    yield return () => Console.WriteLine(item);
    }
    }

  16. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  17. Spot on with this write-up, I truly believe that this web site needs much more attention.
    I’ll probably be back again to see more, thanks for the information!

  18. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  19. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  20. chamo1 said

    what if the collection is a reference type? How do you go about solving it? Moreover, what if you want to update a property of the reference type item? like so:

    var dbEmployees = GetExistingEmployees();

    IList employees = new List { new Employee{ Id = 1, EmpType = new EmployeeType { id = 1},…}, new Employee{ Id = 2, EmpType = new EmployeeType { id = 1},…}}

    foreach (var emp in employees)
    {
    foreach (var oldEmp in dbEmployees)
    {

    if (emp.Id == oldEmp.Id)
    {
    UpdateChanges(r, oldEmp);
    existingEmp.Add(r);
    }
    }
    }

    private void UpdateChanges(Employee emp, Employee oldEmp){

    if (oldEmp.EmpType.Id != emp.EmpType.Id)
    {
    LogChange();

    oldEmp.EmpType.Id != emp.EmpType.Id;
    }
    }

    The issue here is the last employee in the collection if his/her employee type property Id changed in a ddl, updating it will cascade to all other employees’ emp type in the collection. That is nutts. Due to the logging requirement I can not use lambda or other fancy construct. I need hep with fixing this with in foreach or for loops.

    • Bojan Resnik said

      I’m not sure I understand the problem – ultimately, it has nothing to do with lambda or foreach. You might want to ask a question on StackOverflow instead.

      Cheers,
      Bojan

  21. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  22. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  23. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  24. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  25. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  26. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  27. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  28. […] C# lambda and foreach variable […]

  29. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  30. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  31. […] объясняется здесь, это происходит […]

  32. […] All this time though, I knew that there would be some very interesting translations and manipulations that occurred, but I didn’t really consider that the code which reflector spit out might not actually execute in the same way as the code that I was compiling. Well, today I found just that case (in the latest version of Reflector) while checking out this blog post. […]

  33. […] here 설명 된 것처럼 위의 루프 s에서 선언 된 변수 foreach가 컴파일러에서 다음과 같이 변환 되기 때문에 발생 합니다. […]

  34. […] explained here, this happens because the s variable declared in foreach loop above is translated like this in the […]

  35. […] yang dijelaskan di sinihal ini terjadi karena s variabel dideklarasikan dalam foreach loop di atas diterjemahkan seperti […]

  36. […] yang dijelaskan di sini , ini terjadi karena svariabel yang dideklarasikan dalam foreachloop di atas diterjemahkan seperti […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

 
%d bloggers like this: