2009-10-09 47 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

相关问题