5

I was trying to understand the answer for this question Why am I getting wrong results when calling Func<int>? I wrote some sample code. The following code

public static void Main(string[] args) { var funcs = new List<Func<string>>(); for(int v=0,i=0;v<3;v++,i++) { funcs.Add( new Func<string>(delegate(){return "Hello "+ i++ +" "+v;}) ); } foreach(var f in funcs) Console.WriteLine(f()); } 

produces

Hello 3 3 Hello 4 3 Hello 5 3 

After reading the explanation by Jon Skeet and Eric Lippert I thought I will get

Hello 3 3 Hello 3 3 Hello 3 3 

Here both v and i are loop variables, while the value of i is picked up at that instant v is not why is this?. I don't understand the behavior.

2
  • You realise you are incrementing i twice? Commented Mar 25, 2011 at 9:06
  • Yes the increment is intentional. Just I wanted to check. That was the root cause for this question. Commented Mar 25, 2011 at 12:58

4 Answers 4

11

Well, you understood Eric and Jon correctly, but you missed one part of your code:

"Hello "+ i++ +" "+v; ^^^ this part increments i for each call 

So basically, what happens is similar to this:

  1. Capture the anonymous function 3 times, capturing references to variables in the method, not in the scope of the loop
  2. At the end of the loop, those two variables are both at value 3
  3. Execute the first function, outputting the contents of i and v, and then increment i
  4. Execute the second function, outputting the contents of i and v and since this is the same i as the previous method call, you will output 4 here, not 3
  5. and so on

If, on the other hand, you had changed your code by capturing variables inside the loop scope, like this:

for(int v=0,i=0;v<3;v++,i++) { int ii = i, vv = v; funcs.Add( new Func<string>(delegate(){return "Hello "+ ii++ +" "+vv;}) ); } 

Then you would get 0, 0, 1, 1, and 2, 2. You're still increasing the ii variable, you do it after using the captured value in the loop, but then you never use that variable again (each anonymous method gets its own private copy.) thanks @ferosekhanj for the comment

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks lasse for the excellent answer. Now I understood the behavior. One correction with your ii,vv code. we would get 0,1,2 for both. That was the solution to the loop variable capture issue right. Anyway still your answer clarified lot of things.
2

The answer is simple: ++i is executed inside your delegate, thus incrementing the value each time. The first value will be 3 because that's the value of i after the loop.
Understand that your delegate is not executed inside your for loop but in the foreach loop.

Comments

2

the result is correct (how could it not be? ;) ) When you execute the delegate, after the end of the loop, it will use the current value of the i and v variables.

v won't change anymore, v == 3 at the end of the loop. i == 3 too. But your delegate write i to the output, then increment it (i++). Therefore, each time the delegate is executed, i will be incremented, but not v.

This is what you are observing.

Comments

0

I think for loop variables have the scope of the outside of the for loop.

public static void Main(string[] args) { var funcs = new List<Func<string>>(); int i=0; for(int v=0;v<3;v++,i++) { funcs.Add( new Func<string>(delegate(){return "Hello "+ i++ +" "+v;}) ); } foreach(var f in funcs) Console.WriteLine(f()); } 

After the for loop i==3 and v==3. Because the code doesn't leave scope of i between the creating of the delegates all three instances of the delegate share the same i. Thus each call to a function will increment the same i and you get 3,4,5

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.