关于c#:从一个集合中删除的项目也隐含地从第二个集合中删除,为什么?

Item removed from one collection is removed from second collection implicitly too, why?

这对我来说是个大混乱。我有以下代码:

1
2
3
4
5
6
7
8
IEnumerable<WorkflowStateData> data = model.Data.Except(
            request.WorkflowState.CustomData.Select(x => new Model.WorkflowStateData {Key = x.Key}),
            new WorkflowStateDataEqualityComparer());
        foreach (var item in data) // makes the comparison (and exception) based on the value of property 'Key'
        {
            model.Data.Remove(item);
            stateDataSet.Remove(item);
        }

这应该创建新的项集合,并使用foreach将其用于枚举。它应该从原始集合中移除项,但同时从原始集合和"数据"集合中移除项,并引发InvalidOperationException:集合已修改。为什么?


Enumerable.Except返回设定差的IEnumerable,但它延迟了,这意味着它仍然只是查询。所以每当你"触摸"data时,你都会执行这个查询。因此,如果您想要一个"物化"的集合,您必须使用ToListToArrayToLookupToDictionary(或者将foreach循环的结果添加到另一个集合中)。

通常,如果您试图修改当前在foreach中枚举的集合,那么您甚至应该得到一个异常。因此,您可以使用ToList创建一个真正的集合,如果您修改它的源代码,它就不会受到影响:

1
2
3
4
List<WorkflowStateData> data = model.Data.Except(
        request.WorkflowState.CustomData.Select(x => new Model.WorkflowStateData {Key = x.Key}),
        new WorkflowStateDataEqualityComparer())
    .ToList();

现在,您可以从原始集合中删除对象,而不必从列表中删除它。

您应该阅读Linq的延迟执行/渴望评估。


答案很简单,但并不明显,因为使用了叛徒的"var"关键字。except返回IEnumerable,而不是ICollection。IEnumerable可以想象为特定集合的视图,它本身不是集合。因此,当从原始集合中删除项时,我的可枚举项也发生了更改。