RCode

a software development blog by Bojan Resnik

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

10 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?

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>