2012-07-10 19 views
10

Con gran sorprendió he observado lo siguiente comportamiento de hoy: Dada una clasecambio de propiedades de IEnumerator <T> .Current

class Foo 
{ 
    prop int FooNumber { get; set; } 
} 

y este código

IEnumerable<Foo> foos = Enumerable.Range(0,3).Select(new Foo()); 

foreach (var foo in foos) 
    foo.Bar = 5; 

foreach (var foo in foos) 
    Console.Write(foo.Bar); // Writes 000 

al inicializar foos-new List<Foo>{ new Foo(), new Foo(), new Foo() } hace que la escritura de bucle " 555 ".

Mi pregunta: ¿Por qué sucede esto y hay una manera de eludir esto sin usar .ToList() (que necesita un comentario, ya que no parece ser necesario aquí).

+4

Bienvenido al maravilloso mundo de lo que ReSharper llamaría "posible enumeración múltiple". Un enumerable es ** no ** una colección. El hecho de que a veces un enumerable está sobre una colección y puede modificarlo debajo es un efecto secundario. –

Respuesta

20

Ocurre porque foos se produce dinámicamente cada vez que lo enumera. Por lo tanto, durante la primera iteración está estableciendo valores de propiedades en objetos a los que ya no hace referencia ninguna otra cosa una vez que finaliza la iteración. La segunda iteración funciona en objetos recién construidos que tienen el valor de propiedad predeterminado.

Inicialización foos a una lista de objetos "persistentes" cambia las cosas, como lo hace usando .ToList() por la misma razón (una lista "fijo" se construye y se itera más de dos veces, el original producido dinámicamente IEnumerable solamente se repiten a lo largo de una vez).

Una vez establecido que se debe utilizar .ToList() aquí: en general no me siento que necesita un comentario porque no es habitual para repetir secuencias producidas dinámicamente más de una vez (creo que las herramientas de análisis que muchos de código advierten contra esta), pero por supuesto, escriba uno.

+0

Eso lo explica, gracias. Tenía la falsa impresión de que, después de la primera enumeración, el resultado se almacenaría en algún lugar. – Jens

3

Parece obvio lo que está sucediendo: cada vez que enumera, está instanciando nuevos objetos Foo.

si desea que los valores de las propiedades (Foo.Bar) que deben preservarse, entonces usted va a tener que seguir de la Foo algún lugar y ToList() es una forma sencilla de hacer esto.

Cuestiones relacionadas