2009-10-09 53 views
4

我熟悉在使用foreach循環(即「System.InvalidOperationException:集合已被修改」)循環時修改集合的問題。然而,當我使用Linq創建一個從字典中刪除的鍵列表,然後遍歷我的新列表時,我得到相同的異常,這對我沒有任何意義。使用Linq生成要從另一個集合中刪除的東西的集合

代碼之前,拋出一個異常:後

IEnumerable<Guid> keysToDelete = _outConnections.Where(
    pair => pair.Value < timeoutPoint 
).Select(pair => pair.Key); 

foreach (Guid key in keysToDelete) 
{ 
    ...some stuff not dealing with keysToDelete... 
    _outConnections.Remove(key); 
} 

碼,即工作:

List<Guid> keysToDelete = _outConnections.Where(
    pair => pair.Value < timeoutPoint 
).Select(pair => pair.Key).ToList(); 

for (int i=keysToDelete.Count-1; i>=0; i--) 
{ 
    Guid key = keysToDelete[i]; 
    ...some stuff not dealing with keysToDelete... 
    _outConnections.Remove(key); 
} 

這是爲什麼?我有這樣的感覺,也許我的Linq查詢不是真的返回一個新的集合,而是原始集合的一部分,因此它指責我修改集合keysToDelete,當我從_outConnections中刪除元素時。

更新:以下修復也適用,由於亞當·羅賓遜:

List<Guid> keysToDelete = _outConnections.Where(
    pair => pair.Value < timeoutPoint 
).Select(pair => pair.Key).ToList(); 

foreach (Guid key in keysToDelete) 
{ 
    ...some stuff not dealing with keysToDelete... 
    _outConnections.Remove(key); 
} 
+1

如果我能告訴人們關於LINQ查詢的一件事情,那就是查詢表達式的結果是*查詢本身*,而不是*查詢的結果*。這種常見的誤解是有關StackOverflow上關於LINQ的很大一部分問題的基礎。 – 2009-10-09 17:14:39

+0

當我發現Linq時,我想「嘿,這讓我做了像Ruby一樣的東西!」,而且我仍然在內部按照Ruby的'map'和'select'的方式考慮像'Select'和'Where'這樣的方法,它處理並返回一個新的結果集合。我將不得不從精神上將Ruby方法與類似的Linq方法分離開來。 – 2009-10-09 17:33:25

回答

9

你是正確的。 LINQ使用所謂的「延遲執行」。聲明您的LINQ查詢實際上並沒有做任何事情而不是構建查詢表達式。直到你實際列舉了查詢被評估的列表,並且它使用原始列表作爲源。

但是,打電話ToList()應創建一個全新的名單,與原件無關。檢查異常的調用堆棧以確保它實際上被keysToDelete拋出。

+0

哎呀,忘了我的修復的一部分,涉及添加'ToList'。最初,我剛剛使用了'IEnumerable';更新了問題代碼以反映這一點。 – 2009-10-09 15:11:21

+0

Oho,基於你的第二段,我可以改變我的修改到下列之一:1)刪除'ToList'的東西,只是使用'IEnumerable'的'for'循環或2)保留'ToList'東西並且切換回使用'foreach'循環,因爲新的List是一個單獨的集合。謝謝! – 2009-10-09 15:12:37

+1

您不能使用'for'選項,因爲'IEnumerable'不支持基於索引的訪問。我建議通過ToList()的結果切換到'foreach'。 – 2009-10-09 15:43:56

相關問題