Captura variable Lambda en bucle – ¿Qué sucede aquí?

Estoy tratando de entender, ¿qué pasa aquí? ¿Qué tipo de código produce el comstackdor?

public static void vc() { var listActions = new List(); foreach (int i in Enumerable.Range(1, 10)) { listActions.Add(() => Console.WriteLine(i)); } foreach (Action action in listActions) { action(); } } static void Main(string[] args) { vc(); } 

salida: 10 10 .. 10

De acuerdo con esto , se crearía una nueva instancia de ActionHelper para cada iteración. Entonces, en ese caso, asumiría que debería imprimir 1..10. ¿Alguien puede darme un pseudo código de lo que el comstackdor está haciendo aquí?

Gracias.

En esta linea

  listActions.Add(() => Console.WriteLine(i)); 

la variable i , se captura, o si lo desea, crea un puntero a la ubicación de la memoria de esa variable. Eso significa que cada delegado tiene un puntero a esa ubicación de memoria. Después de esta ejecución del bucle:

 foreach (int i in Enumerable.Range(1, 10)) { listActions.Add(() => Console.WriteLine(i)); } 

por razones obvias, i tengo 10 , por lo que el contenido de memoria que apuntan todos los punteros presentes en Action (s) se convierte en 10.

En otras palabras, i capturan.

Por cierto, debería tener en cuenta que, según Eric Lippert, este comportamiento “extraño” se resolvería en C# 5.0 .

Entonces en el C# 5.0 su progtwig se imprimiría como se esperaba :

 1,2,3,4,5...10 

EDITAR:

No puedo encontrar la publicación de Eric Lippert sobre el tema, pero aquí hay otra:

Cierre en un bucle Revisited

¿Qué tipo de código produce el comstackdor?

Parece que esperas esto:

 foreach (int temp in Enumerable.Range(1, 10)) { int i = temp; listActions.Add(() => Console.WriteLine(i)); } 

Eso utiliza un valor diferente para cada iteración, por lo que se capturará el valor de la variable en el momento en que se crea la lambda. De hecho, podría usar este código exacto y obtener el resultado que desea.

Pero lo que el comstackdor realmente hace es algo más cercano a esto:

 int i; foreach (i in Enumerable.Range(1, 10)) { listActions.Add(() => Console.WriteLine(i)); } 

Esto deja en claro que está capturando la misma variable en cada iteración. Cuando más tarde vaya y realmente ejecute ese código, todos se refieren al mismo valor que ya se ha incrementado hasta 10.

Esto no es un error en el comstackdor o en el tiempo de ejecución … fue una decisión consciente por parte del equipo de diseño de idiomas. Sin embargo, debido a la confusión sobre cómo funciona esto, han revertido esa decisión y han decidido arriesgarse a un cambio radical para que funcione más de lo que espera para C # 5 .

En esencia, lo que está sucediendo es que el comstackdor está declarando su int i fuera del ciclo y, por lo tanto, no está creando un nuevo valor para cada acción, simplemente haciendo referencia al mismo cada vez. En el momento en que ejecuta el código i es 10 y entonces imprime 10 mucho.