2008-10-22 21 views
48

Estaba escribiendo algo de código rápido y se dio cuenta de este error compilador¿Por qué es malo para utilizar una variable de iteración en una expresión lambda

Utilizando la variable de iteración en una expresión lambda puede tener resultados inesperados.
En su lugar, cree una variable local dentro del ciclo y asígnele el valor de la variable de iteración.

Sé lo que significa y puedo solucionarlo fácilmente, no es gran cosa.
Pero me preguntaba por qué es una mala idea usar una variable de iteración en una lambda.
¿Qué problemas puedo causar más adelante?

+0

relacionado: http://stackoverflow.com/questions/190227/building-a-linq-query-programatically-without-local-variables-tricking-me – nawfal

+0

mejor si da un ejemplo de dónde realmente funciona/da el resultado correcto! por ejemplo, mira el resultado aquí http://pastebin.com/raw/FghmXkby no está bien ... consistentemente el mismo resultado erróneo. – barlop

Respuesta

50

Contras idee este código:

List<Action> actions = new List<Action>(); 

for (int i=0; i < 10; i++) 
{ 
    actions.Add(() => Console.WriteLine(i)); 
} 

foreach (Action action in actions) 
{ 
    action(); 
} 

¿Qué esperas que se imprima? La respuesta obvia es 0 ... 9 - pero en realidad imprime 10, diez veces. Es porque solo hay una variable que es capturada por todos los delegados. Es este tipo de comportamiento el que es inesperado.

EDIT: Acabo de ver que estás hablando de VB.NET en lugar de C#. Creo que VB.NET tiene reglas aún más complicadas, debido a la forma en que las variables mantienen sus valores en todas las iteraciones. This post by Jared Parsons brinda cierta información sobre el tipo de dificultades involucradas, aunque ya regresó de 2007, por lo que el comportamiento real puede haber cambiado desde entonces.

+6

En dos palabras: lambda no se evalúa necesariamente durante el bucle, y cuando se llaman la variable de iteración puede estar fuera del alcance, no asignada, o con su valor final (incluso más allá del límite de bucle). – BertuPG

+8

@BertuPG: ¿Cuáles de esas fueron las dos palabras en las que estabas pensando? ;) –

+0

@Joh: oh ... sí ... así que permítanme reemplazar "palabras" con "frases" ^^ – BertuPG

6

Suponiendo que te refieres a C# aquí.

Es por la forma en que el compilador implementa los cierres. Usar una variable de iteración puede causar un problema al acceder a un cierre modificado (tenga en cuenta que dije 'no puedo' causar 'un problema porque a veces no sucede dependiendo de qué más hay en el método, y algunas veces usted realmente quiere acceder al cierre modificado).

Más información:

http://blogs.msdn.com/abhinaba/archive/2005/10/18/482180.aspx

Aún más información:

http://blogs.msdn.com/oldnewthing/archive/2006/08/02/686456.aspx

http://blogs.msdn.com/oldnewthing/archive/2006/08/03/687529.aspx

http://blogs.msdn.com/oldnewthing/archive/2006/08/04/688527.aspx

+1

No es "un cierre por método", es más complicado que eso. –

+0

Sí, me doy cuenta de que leí mal: estaba tratando de parafrasear la situación rápidamente (explica Raymond con más profundidad). Se eliminó la frase ofensiva para que las personas puedan ver los enlaces de más información. –

Cuestiones relacionadas