Search code examples
c#lambdaiterator

Using the iterator variable of foreach loop in a lambda expression - why fails?


Consider the following code:

public class MyClass
{
   public delegate string PrintHelloType(string greeting);


    public void Execute()
    {

        Type[] types = new Type[] { typeof(string), typeof(float), typeof(int)};
        List<PrintHelloType> helloMethods = new List<PrintHelloType>();

        foreach (var type in types)
        {
            var sayHello = 
                new PrintHelloType(greeting => SayGreetingToType(type, greeting));
            helloMethods.Add(sayHello);
        }

        foreach (var helloMethod in helloMethods)
        {
            Console.WriteLine(helloMethod("Hi"));
        }

    }

    public string SayGreetingToType(Type type, string greetingText)
    {
        return greetingText + " " + type.Name;
    }

...

}

After calling myClass.Execute(), the code prints the following unexpected response:

Hi Int32
Hi Int32
Hi Int32  

Obviously, I would expect "Hi String", "Hi Single", "Hi Int32", but apparently it is not the case. Why the last element of the iterated array is being used in all the 3 methods instead of the appropriate one?

How would you rewrite the code to achieve the desired goal?


Solution

  • Welcome to the world of closures and captured variables :)

    Eric Lippert has an in-depth explanation of this behaviour:

    basically, it's the loop variable that is captured, not it's value. To get what you think you should get, do this:

    foreach (var type in types)
    {
       var newType = type;
       var sayHello = 
                new PrintHelloType(greeting => SayGreetingToType(newType, greeting));
       helloMethods.Add(sayHello);
    }