expresiones lambda son, por definición, perezosamente evaluadas para que no sean evaluadas hasta que realmente se llamen. En tu caso por la ejecución de la tarea. Si cierra sobre un local en su expresión lambda, se reflejará el estado del local en el momento de la ejecución. Que es lo que ves Puedes aprovechar esto. P.ej. el bucle for realmente no necesita un nuevo lambda para cada iteración suponiendo, a efectos del ejemplo, que el resultado descrito era lo que pretende usted podría escribir
var i =0;
Action<int> action =() => Debug.Print("Error: " + i);
for(;i<50;+i){
Task.Factory.StartNew(action);
}
por el contrario si hubiese deseado que en realidad impreso "Error: 1"..."Error 50"
podría cambiar el anterior para
var i =0;
Func<Action<int>> action = (x) => { return() => Debug.Print("Error: " + x);}
for(;i<50;+i){
Task.Factory.StartNew(action(i));
}
los primeros se cierra sobre i
y usará el estado de i
en el momento en que se ejecuta la acción y el estado es a menudo va a ser el estado después de que finalice el bucle. En el último caso, i
se evalúa con entusiasmo porque se pasa como argumento a una función. Esta función luego devuelve un Action<int>
que se pasa al StartNew
.
Por lo tanto, la decisión de diseño hace que tanto la evaluación perezosa como la evaluación entusiasta sean posibles. Con pereza, porque los locales se cerraron sobre y con entusiasmo porque se puede obligar a la gente a ser ejecutado por pasarlos como un argumento o como se muestra a continuación declarando otro local con un alcance más corto
for (var i = 0; i < 50; ++i) {
var j = i;
Task.Factory.StartNew(() => Debug.Print("Error: " + j));
}
Todo lo anterior es general para Lambdas. En el caso específico de StartNew
hay realmente una sobrecarga que hace lo hace el segundo ejemplo de manera que se puede simplificar a
var i =0;
Action<object> action = (x) => Debug.Print("Error: " + x);}
for(;i<50;+i){
Task.Factory.StartNew(action,i);
}
¿Por qué habrían de hacerlo? Son asíncronos de todos modos. – Vlad
Posible duplicado de "C# Captured Variable in Loop" http://stackoverflow.com/questions/271440/c-sharp-captured-variable-in-loop –
En mi humilde opinión, terminas haciendo 2 preguntas aquí - aparece el 'real' estar en el título (cómo capturar el valor, de modo que la tarea se ejecute sobre el valor en tiempo de ciclo), pero luego el cuerpo de la pregunta parece enfocarse en "¿por qué estas cosas resultan en valores inesperados?" (el efecto de la captura de cierre significa que todos hacen referencia a la misma variable). Por lo tanto, terminas con la mayoría de las respuestas explicando el comportamiento en lugar de responder a tu pregunta "real" (AFAICT :) –