valores valor reciben que programacion pasaje parametros funciones expresiones argumentos c# lambda

reciben - C#lambda, valor de variable local no tomado cuando piensas?



parametros programacion c# (5)

Dado:

void AFunction() { foreach(AClass i in AClassCollection) { listOfLambdaFunctions.AddLast( () => { PrintLine(i.name); } ); } } void Main() { AFunction(); foreach( var i in listOfLambdaFunctions) i(); }

ahora pensarías que haría el equivalente a:

void Main() { foreach(AClass i in AClassCollection) PrintLine(i.name); }

pero no es así, lo que hará es imprimir el nombre del último elemento en AClassCollection todo el tiempo. así que, básicamente, se estaba utilizando el mismo artículo en cada función lambda. sospechaba que podría haber algún retraso en "cuando se creó el lambda" o "cuando tomó una instantánea de las variables externas utilizadas en él", o básicamente, simplemente sosteniendo una "referencia a la variable local" i ''

así que hice esto:

string astr = "a string"; AFunc fnc = () => { System.Diagnostics.Debug.WriteLine(astr); }; astr = "chagnged!"; fnc();

y sorpresa, sorpresa, produce "¡cambiado!"

Estoy usando XNA 3.1 (cualquiera que sea c #)

¿Que esta pasando? ¿la función lambda almacena de alguna manera una "referencia" a la variable o algo así? ¿Hay alguna forma de evitar este problema?


¿Que esta pasando? ¿la función lambda almacena de alguna manera una "referencia" a la variable o algo así?

exactamente eso; Las variables capturadas c # son para la variable , no el valor de la variable. Por lo general, puede evitar esto al introducir una variable temporal y vinculante para eso:

string astr = "a string"; var tmp = astr; AFunc fnc = () => { System.Diagnostics.Debug.WriteLine(tmp); };

especialmente en foreach donde esto es notorio.


¿la función lambda almacena de alguna manera una "referencia" a la variable o algo así?

Cerca. La función lambda captura la variable en sí misma. No es necesario almacenar una referencia a una variable, y de hecho, en .NET es imposible almacenar permanentemente una referencia a una variable. Simplemente captura la variable completa . Nunca capturas el valor de la variable.

Recuerde, una variable es una ubicación de almacenamiento. El nombre "i" se refiere a una ubicación de almacenamiento particular, y en su caso, siempre se refiere a la misma ubicación de almacenamiento.

¿Hay alguna forma de evitar este problema?

Sí. Crea una nueva variable todo el tiempo a través del ciclo. El cierre luego captura una variable diferente cada vez.

Este es uno de los problemas más frecuentemente reportados con C #. Estamos considerando cambiar la semántica de la declaración de la variable de bucle para que se cree una nueva variable cada vez a través del bucle.

Para obtener más detalles sobre este tema, consulte mis artículos sobre el tema:

http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/


Este es un cierre modificado

Ver: preguntas similares como acceso a cierre modificado

Para solucionar el problema, debe almacenar una copia de la variable dentro del alcance del ciclo for:

foreach(AClass i in AClassCollection) { AClass anotherI= i; listOfLambdaFunctions.AddLast( () => { PrintLine(anotherI.name); } ); }


Me ha atrapado también esta, como dijo Calgary Coder, es un cierre modificado. Realmente tuve problemas para detectarlos hasta que me puse de nuevo. Como es una de las advertencias que resharper busca, estoy mucho mejor identificándolas a medida que codigo.


Sí, la lambda almacena una referencia a la variable (conceptualmente hablando, de todos modos).

Una solución muy simple es esta:

foreach(AClass i in AClassCollection) { AClass j = i; listOfLambdaFunctions.AddLast( () => { PrintLine(j.name); } ); }

En cada iteración del ciclo foreach, se crea una nueva j , que captura el lambda. i por otro lado, es la misma variable en todo, pero se actualiza con cada iteración (para que todas las lambdas terminen viendo el último valor)

Y estoy de acuerdo en que esto es un poco sorprendente. :)