2011-08-20 10 views
5

Considera este código.¿Cómo difieren los cierres entre foreach y list.ForEach()?

 var values = new List<int> {123, 432, 768}; 

     var funcs = new List<Func<int>>(); 

     values.ForEach(v=>funcs.Add(()=>v)); 

     funcs.ForEach(f=>Console.WriteLine(f()));//prints 123,432,768 

     funcs.Clear(); 

     foreach (var v1 in values) 
     { 
      funcs.Add(()=>v1); 
     } 

     foreach (var func in funcs) 
     { 
      Console.WriteLine(func()); //prints 768,768,768 
     } 

sé que la segunda imprime foreach 768 3 veces a causa de la variable de cierre capturados por el lambda. ¿Por qué no ocurre en el primer caso? ¿Cómo se diferencia la palabra clave foreach del método Foreach? ¿Es por culpa se evalúa la expresión cuando lo hago values.ForEach

Respuesta

9

foreach sólo se introduce una variable. Mientras que la variable del parámetro lambda es "nueva" cada vez que se invoca.

Comparar con:

foreach (var v1 in values) // v1 *same* variable each loop, value changed 
{ 
    var freshV1 = v1; // freshV1 is *new* variable each loop 
    funcs.Add(() => freshV1); 
} 

foreach (var func in funcs) 
{ 
    Console.WriteLine(func()); //prints 123,432,768 
} 

Es decir,

foreach (T v in ...) { } 

puede ser pensado como:

T v; 
foreach(v in ...) {} 

feliz de codificación.

+1

Gracias. +1 para la escritura. –

6

La diferencia es que en el bucle foreach, usted tiene una sola variable de v1 que es capturado. Esa variable toma cada valor dentro de values - pero usted es solo usando al final ... lo que significa que solo vemos el valor final cada vez.

En su versión , cada iteración introduce una nueva variable (el parámetro f), por lo que cada expresión lambda captura una variable separada, que nunca cambia de valor.

Eric Lippert tiene blogged about this - pero tenga en cuenta que este comportamiento puede cambiar en las versiones futuras de C#.

+0

Entiendo la semántica 'foreach'. Pero en 'List .ForEach' ¿quiere decir que la variable' v' como en la tercera línea se acaba de crear para cada iteración? –

+0

@ Ashley: Sí, porque es el parámetro para la expresión lambda. Incluso si invocó el mismo delegado dos veces y la expresión lambda * modificó * el valor, no haría ninguna diferencia, porque no es una variable capturada, solo un parámetro de delegado. –

+0

@Jon, ¿es posible que cambien ese comportamiento sin introducir regresiones importantes? – FuleSnabel

Cuestiones relacionadas