2011-03-25 8 views
5

Estaba tratando de entender la respuesta a esta pregunta Why am I getting wrong results when calling Func<int>? Escribí un código de muestra. El siguiente código¿por qué este programa C# produce tal resultado? ¿Cómo entiendo el cierre?

public static void Main(string[] args) 
{ 
    var funcs = new List<Func<string>>(); 
    for(int v=0,i=0;v<3;v++,i++) 
    { 
     funcs.Add(new Func<string>(delegate(){return "Hello "+ i++ +" "+v;})); 
    } 
    foreach(var f in funcs) 
     Console.WriteLine(f()); 
} 

produce

Hello 3 3 
Hello 4 3 
Hello 5 3 

Después de leer la explicación de Jon Skeet y Eric Lippert pensé voy a tener

Hello 3 3 
Hello 3 3 
Hello 3 3 

Aquí, tanto V e I son variables de bucle, mientras el valor de i se recoge en ese instante v no es por qué es esto ?. No entiendo el comportamiento.

+0

¿Se da cuenta de que está incrementando 'i' dos veces? – Blorgbeard

+0

Sí, el incremento es intencional. Solo quería comprobar. Esa fue la causa raíz de esta pregunta. – ferosekhanj

Respuesta

11

Bueno, usted entiende Eric y Jon correctamente, pero se perdió una parte de su código:

"Hello "+ i++ +" "+v; 
      ^^^ 
      this part increments i for each call 

Así que, básicamente, lo que sucede es similar a esto:

  1. captura de la función anónima 3 veces, capturando referencias a variables en el método, no en el alcance del ciclo
  2. Al final del ciclo, esas dos variables están ambas en el valor 3
  3. Ejecutar la primera función, la salida de los contenidos de i y v, y luego incrementar i
  4. ejecutar la segunda función, la salida de los contenidos de i y v y dado que este es el mismo i como la llamada al método anterior, que es la salida 4 aquí, no 3
  5. y así sucesivamente

Si, por el contrario, que había cambiado su código de captura por variables dentro del alcance de bucle, como esto:

for(int v=0,i=0;v<3;v++,i++) 
{ 
    int ii = i, vv = v; 
    funcs.Add(new Func<string>(delegate(){return "Hello "+ ii++ +" "+vv;})); 
} 

Luego obtendrá 0, 0, 1, 1 y 2, 2. Aún está aumentando la variable ii, lo hace después de usar el valor capturado en el bucle, pero luego nunca usa esa variable otra vez (cada método anónimo obtiene su propia copia privada) gracias @ferosekhanj por el comentario

+1

Gracias lasse por la excelente respuesta. Ahora entendí el comportamiento. Una corrección con tu código ii, vv. obtendríamos 0,1,2 para ambos. Esa fue la solución al problema de captura de variables de bucle derecha. De todos modos, tu respuesta clarificó muchas cosas. – ferosekhanj

+0

* doh *, corregido el último bit, ¡gracias! –

2

La respuesta es simple: ++i se ejecuta dentro de su delegado, incrementando así el valor cada vez. El primer valor será 3 porque ese es el valor de i después del bucle.
Comprenda que su delegado no se ejecuta dentro de su bucle for, sino en el bucle foreach.

2

el resultado es correcto (¿cómo podría no serlo?;)) Cuando ejecuta el delegado, después del final del ciclo, utilizará el valor actual de las variables i y v.

v no cambiará más, v == 3 al final del ciclo. yo == 3 también. Pero su delegado escribe i en el resultado, luego lo incrementa (i ++). Por lo tanto, cada vez que se ejecuta el delegado, aumentaré, pero no v.

Esto es lo que está observando.

0

Creo que las variables de bucle tienen el alcance del exterior del bucle for.

public static void Main(string[] args) 
{ 
    var funcs = new List<Func<string>>(); 
    int i=0; 
    for(int v=0;v<3;v++,i++) 
    { 
     funcs.Add(new Func<string>(delegate(){return "Hello "+ i++ +" "+v;})); 
    } 
    foreach(var f in funcs) 
     Console.WriteLine(f()); 
} 

Después de que el bucle for i==3 y v==3. Debido a que el código no deja el alcance de i entre la creación de los delegados, las tres instancias del delegado comparten el mismo i. Por lo tanto, cada llamada a una función aumentará el mismo i y obtendrá 3, 4, 5

Cuestiones relacionadas