2010-05-07 84 views
13

我有兩個字符串集合:CollectionA是存儲在系統中的對象的StringCollection屬性,而CollectionB是運行時生成的List。如果有任何區別,CollectionA需要更新以匹配CollectionB。所以我設計了我期望成爲一個簡單的LINQ方法來執行刪除。當不修改枚舉集合時,爲什麼會出現「集合已被修改;枚舉操作可能無法執行」?

var strDifferences = CollectionA.Where(foo => !CollectionB.Contains(foo)); 
foreach (var strVar in strDifferences) { CollectionA.Remove(strVar); } 

但我得到的strDifferences一個"Collection was modified; enumeration operation may not execute"錯誤...即使它是從集合被修改單獨枚舉!我最初設計了這個明確來規避這個錯誤,因爲我的第一個實現會產生它(因爲我在CollectionA之間列舉並且僅僅在!CollectionB.Contains(str)時才刪除)。任何人都可以瞭解爲什麼這個枚舉失敗了嗎?

回答

21

這兩者並不完全分離。 Where不會製作集合的單獨副本。它在內部保留對原始集合的引用,並在請求它們時從中獲取元素。

您可以通過添加ToList()來強制Where立即遍歷集合來解決您的問題。

var strDifferences = CollectionA 
    .Where(foo => !CollectionB.Contains(foo)) 
    .ToList(); 
+0

'ToArray()'會在這裏變得更好嗎?沒有必要使用'List'。 – svick 2010-05-07 21:22:44

+0

@svick這是我一直想知道的事情,但那會是我在另一天要問的問題。提供它還沒有被問到,當然 - - 點擊 - – 2010-05-07 21:24:46

+1

@GraceNote在這裏捕獲:http://stackoverflow.com/questions/1105990/is-it-better-to-call-tolist-or-toarray-in -linq-queries – nawfal 2013-06-05 14:27:16

3

Where回傳IEnumerable<T>,然後你正在使用您的foreach (var strVar in strDifferences)

然後試圖從創建IEnumerable<T>集合中刪除它。你還沒有創建一個新的列表,它引用CollectionA來拉下一個項目,所以你不能編輯CollectionA。既然你要刪除不在CollectionB的那些

var strDifferences = CollectionA.Where 
    (foo => CollectionB.Contains(foo)).ToList(); 

CollectionA = strDifferences; 
//or instead of reassigning CollectionA 
CollectionA.Clear(); 
CollectionA.AddRange(strDifferences); 

你也可以做到這一點。只要查找那些,創建一個列表並將該列表分配給CollectionA變量。

+0

嗯......我很樂意使用你的替代方案(但實際上效果相同),但不幸的是,CollectionA的結果是隻讀屬性,所以我無法設置它。不過,你可以給出更深入的解釋,所以+1。 – 2010-05-07 20:50:18

+0

@ccornet如果不能設置它,你可以清除它並重新添加項目。 – kemiller2002 2010-05-07 21:00:30

+0

我想了很久,很努力。我決定仍然採用刪除方法。我不希望修改CollectionA,如果沒有必要進行更改,建立一個需要刪除的內容的列表可以使其非常快速且簡單地知道(只要看看strDifferences.Count> 0)。如果我構建了一個目標數組,我仍然必須檢查CollectionA是否有需要刪除的內容。 – 2010-05-10 12:53:26

3

嘗試修改第一線位:

var strDifferences = 
    CollectionA.Where(foo => !CollectionB.Contains(foo)).ToList(); 

我認爲LINQ是使用您的查詢的懶惰執行。對ToList的調用將在枚舉之前強制執行查詢。

+0

是的,並確保不要把它放在兩行i..e。 var strDifferencesTemp = CollectionA.Where(foo =>!CollectionB.Contains(foo)); var strDifferences = strDifferencesTemp.ToList(); 如果這樣做,集合可以通過這兩行之間的其他線程進行修改。 – Markus 2017-06-02 16:48:46

相關問題