为什么使用add range将一个列表添加到另一个列表中时,会删除第一个列表中的元素?

6
考虑以下示例:
IEnumerable<Int32> groupsToAdd = new List<Int32>();

List<Int32> groups1 = new List<Int32>() { 1,2,3 };
List<Int32> groups2 = new List<Int32>() { 3,4,5 };

groupsToAdd = groups1.Where(g => false == groups2.Contains(g));

groups2.AddRange(groupsToAdd);

groupsToAdd.Dump();

当调用groupsToAdd.Dump()时,列表现在为空。我查阅了AddRange的参考文献,它没有提到元素从列表中删除,但当我测试这段代码(在linqpad中)时,它最终为空。为什么会这样呢?
编辑: 澄清一下,我的意思是元素正在从groupsToAdd中删除,因为在groups2.AddRange(groupsToAdd)之前,groupsToAdd已经填充了两个元素。

1
你没有展示给我们所有的代码。你上面的代码并没有改变groups1。 - itsme86
2
@itsme86 不需要。 - Servy
1
@itsme86 这是完整的代码,这段代码本身将在linqpad和单元测试中“Dump”一个空列表。 - David Work
1
如果您使用groupsToAdd = groups1.Where(g => false == groups2.Contains(g)).ToList();,则将查询实例化为List<int>。然后它的行为就像预期的那样,因为不再有延迟执行。 - Tim Schmelter
1
@itsme86: “当调用groupsToAdd.Dump()时,列表现在为空。”他认为groupsToAdd是一个列表,这是不正确的。 - Tim Schmelter
显示剩余4条评论
2个回答

15

当使用LINQ时,需要记住的重要事情是它会生成一个查询,而不是该查询的结果。 groupsToAdd 不是项目列表,而只是能够在需要时获取某些项目的查询定义。

groupsToAdd 实际上并不迭代源序列(即groups1),也不执行谓词检查(这取决于groups2的状态),直到被迭代为止。

你两次迭代了groupsToAdd。第一次是通过调用AddRange,第二次是通过调用Dump。第二次迭代时,group2已更改,因此查询结果也已更改。

如果你想避免这种延迟执行,则可以立即实现查询,修改你的代码如下:

groupsToAdd = groups1.Where(g => false == groups2.Contains(g));
    .ToList();

这将在那个时间点评估查询,以便groupsToAdd将代表查询结果而不是查询本身


这应该是被接受的答案。它清楚地解释了 OP 的问题。 - Stack Undefined

12

这是因为 IEnumerable 的缘故。当你将 groupsToAdd 设置为 groups1.Where(g => false == groups2.Contains(g)) 的结果时,它是延迟执行的,这意味着查询直到 AddRange() Dump() 才运行。由于列表 groups2 现在包含元素,它们不再是原始查询的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接