2011-09-03 11 views
6

Hola, tengo el siguiente código que produce un comportamiento extraño. Una propiedad de una instancia de objetos contenidos en un IEnumerable producido por linq a Objects, no se actualiza en declaraciones foreach posteriores. La declaración de foreach debe enuemerate el IEnumerable. En cambio, la solución es enumerarlo antes.Comportamiento extraño en linq C# en la ejecución retrasada

Aunque encontré la solución, no he visto esto documentado en ningún lugar, ni en libros ni en artículos, tratando con ejemplos similares. Quizás alguien con un conocimiento complejo de linq pueda explicarlo.

Me llevó un día identificar la causa exacta del error y no es fácil de depurar en una aplicación grande. Luego lo reproduje en un entorno mucho más simple, presentado a continuación.

public class MyClass 
{ 
    public int val ; 
} 

public class MyClassExtrax 
{ 
    public MyClass v1 { get; set; } 
    public int prop1 { get; set; } 
} 

void Main() 
{ 
List <MyClass> list1 = new List<MyClass>(); 
MyClass obj1 = new MyClass(); obj1.val = 10; 
list1.Add(obj1); 
MyClass obj2 = new MyClass(); 
obj2.val = 10; 
list1.Add(obj2); 

IEnumerable<MyClassExtrax> query1 = 
    from v in list1 
    where v.val >= 0 
    select new MyClassExtrax{ v1=v , prop1=0 } ; 

//query1=query1.ToList(); solves the problem..but why is this needed..? 
foreach (MyClassExtrax fj in query1) 
    { 
    fj.v1.val = 40; 
    fj.prop1 = 40; //property does not get updated.. 
    } 


foreach (MyClass obj in list1) 
    { 
    Console.WriteLine("in list 1 value is {0} : ", obj.val); 
    } 


foreach (MyClassExtrax obj in query1) 
    { 
    Console.WriteLine("in MyClassExtra list v1.val is {0}, prop1 is {1} ", obj.v1.val, obj.prop1); 
    } 

} 

salida: en la lista 1 valor es 40:

en la lista 1 valor es 40:

en MyClassExtra lista v1.val es 40, prop1 es 0

en MyClassExtra lista v1.val es 40, prop1 es 0

Como puede ver, prop1 no se actualiza a 40.

Respuesta

7

Esto es bastante simple, es well-documented. Esto se debe al deferred execution feature de LINQ. Este código

IEnumerable<MyClassExtrax> query1 = 
from v in list1 
where v.val >= 0 
select new MyClassExtrax{ v1=v , prop1=0 } ; 

en realidad no crea los objetos. Simplemente crea un objeto que cuando se itera en realidad crea el objeto. Es decir, puede pensar en query1 como la regla que sabe cómo escupir los objetos cuando se solicitan. Así que cuando usted hace esto:

foreach (MyClassExtrax fj in query1) 
{ 
    fj.v1.val = 40; 
    fj.prop1 = 40; //property does not get updated.. 
} 

y luego esto:

foreach (MyClassExtrax obj in query1) 
{ 
    Console.WriteLine("in MyClassExtra list v1.val is {0}, prop1 is {1} ", obj.v1.val, obj.prop1); 
} 

se está ejecutando el regla para generar los objetos en dos ocasiones. Es decir, se producen dos secuencias distintas de objetos y no se comparten entre las secuencias. Es por eso que no ve los valores actualizados; las referencias a través de las dos iteraciones de la secuencia no son las mismas.

Sin embargo, cuando llama al ToList, y luego recorre la lista resultante, ahora solo ha producido una secuencia de objetos, y esa secuencia de objetos es obviamente la misma en las dos iteraciones.

+0

Gracias, tienes razón. La propiedad se actualiza por ejemplo, al insertar Console.WriteLine ("en MyClassExtra list v1.val es {0}, prop1 es {1}", fj.v1.val, fj.prop1); en el primer ciclo Solo en el segundo ciclo for está creando una nueva referencia a las instancias de MyClassExtrax. – gregor

Cuestiones relacionadas