我知道在您进行枚举时更改集合会导致collection was modified exception
。
但是如果我从较大的子集中得到一个子集合,而我在枚举该子列表时从一个较大的子集删除了一些项目,我仍然会收到此错误。在子集合上调用ToList
解决了这个问题。但为什么会发生呢?
var localCollection = someData.ToList(); // from DB Context
var localGrouped = localCollection.GroupBy(x => x.Id).Select(g => new { Id = g.Key, List = g.Select(x => x.Value) }); or .ToList(); // Here how I solve exception
var groups = new List<List<Int64>>();
while (localGrouped.Any())
{
var newSelected = new List<Int64>();
var firstGroup = localGrouped.First();
newSelected.Add(firstGroup.Id);
localGrouped.Remove(firstGroup);
var similiarGroups = localGrouped.Where(x => x.List.Intersect(firstGroup.List).Any()).ToList();
if (similiarGroups.Any())
{
foreach (var similiarGroup in similiarGroups)
{
//Changing something here in parent collection causes exception
newSelected.Add(similiarGroup.Id);
localGrouped.Remove(similiarGroup);
}
}
groupsOfParcels.Add(newSelected);
}
答案 0 :(得分:7)
Where
不是子集合,它是一个过滤器。不同之处在于LINQ如何运作的核心。
你可以认为Where
粗略地保存了对原始IEnumerable
的引用,以及一些检查条件的代码,以及一些说明它已走了多远的状态。当您对getNext()
的输出执行Where
时,该段代码将遍历原始IEnumerable
,直到找到满足条件的元素,然后将其返回(或结束)原始IEnumerable
,这意味着它也在Where
的末尾。
这是延迟评估 - 它只会在任何时候查看所需的任意数量的字词。因此原始的IEnumerable
必须存在并且在整个过程中不进行修改。如果您致电ToList()
,评估将立即进行 - 所有元素将被提取并放入列表中,然后继续。
关于这一点的最好的东西是C#和Linq神Jon Skeet。他的帖子包括重新实现所有主要的Linq功能,并详细讨论实现问题,以便您可以确切地了解它们(可能)的工作方式。
答案 1 :(得分:5)
GroupBy
,Where
,Select
以及大多数其他LINQ操作只是从底层集合中抓取IEnumerable
并进行迭代。底层IEnumerable
在尝试获取下一个项目时抛出异常,因为该集合已被修改。通过每个LINQ运算符抛出该异常,因为如果它无法从基础序列中获取下一个项目,则它无法完成其工作。
通过使用ToList
,您可以在修改集合之前强制整个序列,而不是允许将基础列表的枚举推迟到该列表之后修改。