2011-09-03 51 views
6

嗨,我有下面的代碼,它會產生一個奇怪的行爲。 Linq生成的IEnumerable中包含的對象實例的屬性在後續的foreach語句中不會更新。 foreach語句應該枚舉IEnumerable。相反,解決方案是先枚舉它。在延遲執行LINQ C#中的奇怪行爲

雖然我找到了解決方案,但我沒有看到這個文檔記載在書籍或文章的任何地方,處理類似的例子。也許有人對linq有着複雜的認識可以解釋它。

我花了一天時間查明錯誤的確切原因,並且在大型應用程序中調試並不容易。然後我在一個更簡單的環境中重現它,如下所示。

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

} 

輸出: 列表1級中值爲40:

列表1級中值爲40:

在MyClassExtra列表v1.val

是40,PROP1是0

在MyClassExtra

list v1.val是40,prop1是0

正如你所看到的,prop1不會更新到40。

回答

7

這很簡單,它是well-documented。這是因爲LINQ的deferred execution feature。此代碼

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

實際上並未創建對象。它只是創建一個對象,當迭代時實際創建對象。也就是說,您可以將query1視爲知道如何在請求時吐出對象的規則。所以,當你這樣做:

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

,然後這樣的:

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

您正在執行的規則產生兩倍的對象。也就是說,生成兩個不同的對象序列,並且它們不在序列之間共享。這就是爲什麼你看不到更新的價值;跨越序列的兩次迭代的參考是不相同的。

但是,當您調用ToList,然後遍歷結果列表時,現在只生成了一個對象序列,並且該對象序列在兩次迭代中顯然是相同的。

+0

謝謝你是對的。該屬性確實得到更新。例如當插入Console.WriteLine時(「在MyClassExtra列表中v1.val是{0},prop1是{1}」,fj.v1.val,fj.prop1);在第一個循環中。就在第二個循環中,它創建了一個MyClassExtrax實例的新參考。 – gregor