References to C # Objects and Action Types

I have a quick question about Action and Lambdas types in C #. Here is the code:

  static void Main(string[] args) { List<Action> actions = new List<Action>(); for (int I = 0; I < 10; I++) actions.Add(new Action(() => Print(I.ToString()))); foreach (Action a in actions) { a.Invoke(); } actions.Clear(); int X; for (X = 0; X < 10; X++) { int V = X; actions.Add(new Action(() => Print(V.ToString()))); } foreach (Action a in actions) { a.Invoke(); } Console.ReadLine(); } public static void Print(string s) { Console.WriteLine(s); } 

If you run this code, you will see that it outputs 10, ten times in a row, and then outputs the numbers 0-9 a second time. This clearly relates to how I use X vs I, and how I give my action to a new variable V every time in the second loop ... Maybe every new V is a new address in memory, but I'm trying to understand why I. ToString () does not do the same in the first loop ... Why does IToString () used in the first action work the same as the second example?

+5
source share
3 answers

The for loop is effectively extended to this by the compiler:

 { int I; for (I = 0; I < 10; I++) { actions.Add(new Action(() => Print(I.ToString()))); } } 

This means that all lambda instances capture the same instance of I , which will be 10 when the loop is complete.

In your second example, you copy the value to a variable that is bound to the body of the for statement, and the lambda captures that local value. For each repetition of the loop there will be a unique local one.

It is important to realize that you are not fixing the value of a variable, but fixing this variable yourself. That is why the first example does not work, but the second does.

+5
source

This is because it is just a delegate, and it starts when you call it, and at the time it is called, all actions have the last value that was set for i , whereas in the case of the foreach loop, a local copy of the value, so each action has its own value which makes printing 0-9.

In the first example, the value of i is evaluated the first time the delegate is called in the foreach loop, and at that time i has 10 , and in the second example, you store the value in local, which simulates the same behavior that is performed using the foreach loop, since the foreach loop also creates copy value.

you can also read this explanation by Jon Skeet , and there he links with 2 eric-lipper messages to help you more.

+2
source

When an Action is created in the first loop, what is actually saved is a call to the Print() method, and the value of the variable I not obtained until invocations of the Invoke() method are executed, but this happens after the loop ends and the variable I has value 10.

In the second case, you create a new variable V at each iteration, so that when you perform the actions, each of them is called with the value of the variable V created in this iteration.

+1
source

Source: https://habr.com/ru/post/1243160/


All Articles