C# – Lambda expressions and access to modified closure

When I use Lambda expressions, I find out a strange and unexpected behavior. Let’s consider the following example

static ICollection<Func<string>> CreateExample1()
{
	ICollection<Func<string>> lstFunctions = new LinkedList<Func<string>>();
	for (int nIndex = 0; nIndex < cnst_nCount; nIndex++)
	{
		lstFunctions.Add(() => nIndex.ToString());
	}
	return lstFunctions;
}

static void Print(ICollection<Func<string>> lstFunctions)
{
	Console.WriteLine(string.Join(", ", lstFunctions.Select(f => f()).ToArray()));
}

What are we waiting for when calling Print(CreateExample1())? An output with “0, 1, 2, 3″? The answer is no. When we compile and run the code above we will have a black screen with array of “4, 4, 4, 4″. Why doesn’t it run correctly? Let’s go back to review how the Lambda expression work.

A lambda expression is an anonymous function that can contain expressions and statements, and can be used to create delegates or expression tree types.
All lambda expressions use the lambda operator =>, which is read as “goes to”. The left side of the lambda operator specifies the input parameters (if any) and the right side holds the expression or statement block
.
The important feature of lambda expression that we must know is that it is evaluated during run-time. That means, in the example above, the lambda expression will be “compiled and executed” only when the for-loop starts to run. Therefore when the expression is being evaluated, the value of nIndex variable was changed and in our case the nIndex reaches it up-bound already when the evaluation of expression is going on.
To solve this problem, what we should do is pushing our variable nIndex into stack and when the expression is evaluated, it will pop the value from stack for its calculation.
To push value into stack, we can use a temporary variable to store current value of iteration variable.

static ICollection<Func<string>> CorrectExample()
{
	ICollection<Func<string>> lstFunctions = new LinkedList<Func<string>>();
	for (int nIndex = 0; nIndex < cnst_nCount; nIndex++)
	{
		int j = nIndex;
		lstFunctions.Add(() => j.ToString());
	}
	return lstFunctions;
}

So in the end of this blog, remember this hint “Using the iteration variable in a lambda expression may have unexpected results. Instead, create a local variable within the loop and assign it the value of the iteration variable.

2 thoughts on “C# – Lambda expressions and access to modified closure”

  1. Not only Lambda has the problem, almost language support anonymous method has the problem, below is an example in javascript:


    function init() {
    window[\funcs\] = {};
    for(var i = 1; i < 5; i++) {
    window.funcs["func_" + i] = function() {
    alert(i);
    }
    }
    }
    function test() {
    for(var name in window.funcs) {
    window.funcs[name]();
    }
    }

    init();
    test();

  2. Thanks. I just ran into this and came to this conclusion and you corroborated my finding. I didn’t know about the variable declaration within the loop being used to solve the problem. Good to have in my toolbox.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>