在遍历列表时向列表中添加项目是否安全

本文关键字:列表 项目 是否 添加 安全 遍历 | 更新日期: 2023-09-27 18:30:11

我知道不能同时使用枚举器和修改List<T>。添加或删除项将使枚举器无效(http://msdn.microsoft.com/en-us/library/system.collections.ienumerator(v=vs.110).aspx),因为内部数组将被重新分配(http://msdn.microsoft.com/en-us/library/3wcytfd1(v=vs.110).aspx).

  1. 即使Count小于Capacity,它总是真的吗?(据我所知,在这种情况下,数组不会被重新分配,因此枚举器必须有效)
  2. 为什么Current返回它设置为的元素,即使枚举器已经无效?(我的意思是,如果数组被重新分配…)
  3. 重新分配是否保留了项目的原始顺序?我的意思是,如果某个项目的索引为n,那么在添加和项目后,它会有相同的索引吗?(MSDN说Add在列表末尾添加了一个对象)如果是这样,那么在常规的for循环中运行列表并添加项目是安全的,假设循环只遍历每个项目一次,我是对的吗

在遍历列表时向列表中添加项目是否安全

即使Count小于Capacity ,它总是真的吗

这可能是真的,也可能不是真的。但是,您应该表现得好像它始终是真的一样,因为文档不会对此行为做出任何例外。

在每次更改列表的操作中,至少有一个List<T>实现(来自Mono项目)总是通过递增存储在列表中的名为_version的隐藏成员来使所有迭代器无效。当您获得迭代器(在.NET中称为Enumerator)时,_version的当前值存储在枚举器对象中。每次调用MoveNext时,都会将存储的_version与当前的_version进行比较,当两者不匹配时会引发异常。

为什么Current返回它设置为的元素,即使枚举器已经无效?

因为Current的值存储在迭代器中。虽然在修改集合时有充分的理由阻止您进一步迭代,但当迭代器有效时,允许您访问迭代器前进位置的值也是可以的。

重新分配是否保留了项目的原始顺序?

是的。这就是为什么即使在列表中添加或删除元素时,使用for循环和索引遍历列表仍然是安全的。

  1. 内部数组不会在每次从列表中添加/删除项时重新分配,这不是枚举器无效的原因。这里有一个更好的理由:集合被修改了;枚举操作可能无法执行-为什么?。

    简单地说,内部阵列在其容量耗尽时被重新分配,阵列的大小将增加一倍。某些集合还允许修剪内部数组,例如List<T>.TrimExcess

  2. 为什么不呢?该对象仍然存在。

如果您想了解更多信息,请查看源代码,特别是EnsureCapacityList<T>.Enumerator

添加或删除项将使枚举器无效,因为内部阵列将被重新分配

这部分是正确的。添加、删除或插入项将使枚举器失效而不是,因为内部数组将被重新分配,但因为List<T>在内部维护一个version字段以跟踪已发生的更改。枚举器将使用版本字段查找自创建枚举器以来发生的任何新更新。

回答您的问题:

  1. 如果修改列表而不考虑"计数<容量",则枚举器无效,因为这无关紧要。重要的是版本字段
  2. 因为枚举器从列表中获取当前元素的副本
  3. 是的,重新分配保留订单

1:这并不总是真的,在实现中有一个bug。