2010-05-07 87 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

相关问题