在新的子集合中枚举时,集合被修改为异常

时间:2014-04-30 16:50:13

标签: c# linq exception collections

我知道在您进行枚举时更改集合会导致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);
}

2 个答案:

答案 0 :(得分:7)

Where不是子集合,它是一个过滤器。不同之处在于LINQ如何运作的核心。

你可以认为Where粗略地保存了对原始IEnumerable的引用,以及一些检查条件的代码,以及一些说明它已走了多远的状态。当您对getNext()的输出执行Where时,该段代码将遍历原始IEnumerable,直到找到满足条件的元素,然后将其返回(或结束)原始IEnumerable,这意味着它也在Where的末尾。

这是延迟评估 - 它只会在任何时候查看所需的任意数量的字词。因此原始的IEnumerable必须存在并且在整个过程中不进行修改。如果您致电ToList(),评估将立即进行 - 所有元素将被提取并放入列表中,然后继续。

关于这一点的最好的东西是C#和Linq神Jon Skeet。他的帖子包括重新实现所有主要的Linq功能,并详细讨论实现问题,以便您可以确切地了解它们(可能)的工作方式。

Introduction

Part 2 - Where(!)

答案 1 :(得分:5)

GroupByWhereSelect以及大多数其他LINQ操作只是从底层集合中抓取IEnumerable并进行迭代。底层IEnumerable在尝试获取下一个项目时抛出异常,因为该集合已被修改。通过每个LINQ运算符抛出该异常,因为如果它无法从基础序列中获取下一个项目,则它无法完成其工作。

通过使用ToList,您可以在修改集合之前强制整个序列,而不是允许将基础列表的枚举推迟到该列表之后修改。