2011-09-03 2 views
6

Ciao, Ho il seguente codice che produce uno strano comportamento. Una proprietà di un'istanza di oggetti contenuti in un oggetto IEnumerable prodotto da linq su Objects non viene aggiornata nelle successive istruzioni foreach. L'istruzione foreach dovrebbe enumerare l'oggetto IEnumerable. Invece la soluzione è di enumerarlo prima.Strano comportamento in linq C# in esecuzione ritardata

Anche se ho trovato la soluzione non ho visto questo documentato ovunque nei libri o negli articoli, trattando con esempi simili. Forse qualcuno con una conoscenza intricata di linq può spiegarlo.

Mi ci è voluto un giorno per individuare la causa esatta dell'errore e non è facile eseguire il debug in un'applicazione di grandi dimensioni. L'ho poi riprodotto in un ambiente molto più semplice, presentato di seguito.

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); 
    } 

} 

uscita: nell'elenco 1 valore è 40:

nell'elenco 1 valore è 40:

Nelle MyClassExtra v1.val è 40, prop1 è 0

in MyClassExtra lista v1.val è 40, prop1 è 0

Come si può vedere prop1 non viene aggiornato a 40. !!

risposta

7

Questo è piuttosto semplice, è well-documented. Questo a causa dello deferred execution feature di LINQ. Questo codice

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

non crea effettivamente gli oggetti. Crea solo un oggetto che, una volta iterato, crea effettivamente l'oggetto. Cioè, puoi pensare a query1 come la regola che sa come sputare gli oggetti quando vengono richiesti. Quindi, quando si esegue questa operazione:

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

e poi questo:

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

si sta eseguendo la regola per la generazione degli oggetti due volte. Cioè, vengono generate due sequenze distinte di oggetti, e non sono condivise tra le sequenze. Questo è il motivo per cui non vedi i valori aggiornati; i riferimenti tra le due iterazioni della sequenza non sono gli stessi.

Tuttavia, quando si chiama ToList e quindi si cammina l'elenco risultante, ora si è prodotta solo una sequenza di oggetti e quella sequenza di oggetti è ovviamente la stessa tra le due iterazioni.

+0

Grazie hai ragione. La proprietà viene aggiornata. per esempio quando si inserisce Console.WriteLine ("nell'elenco MyClassExtra v1.val è {0}, prop1 è {1}", fj.v1.val, fj.prop1); nel primo ciclo. Proprio nel secondo ciclo for si sta creando un nuovo riferimento alle istanze di MyClassExtrax. – gregor