在迭代它时将项添加到List是否安全

时间:2014-11-12 14:03:23

标签: c#

我理解,不能使用枚举器并同时修改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循环中列出并添加项目,假设循环将仅遍历每个项目一次,我是否正确?

3 个答案:

答案 0 :(得分:3)

  

即使Count小于Capacity

,也始终如此

这可能是也可能不是。但是,您应该表现得总是如此,因为文档不会对此行为做出任何例外。

至少one implementation of List<T> (from the Mono project)总是通过在每个更改列表的操作中递增存储在列表中的名为_version的隐藏成员来使所有迭代器无效。获取迭代器(在.NET中称为Enumerator)时,_version的当前值存储在枚举器对象中。每次调用MoveNext时,都会将存储的_version与当前_version进行比较,并在两者不匹配时抛出异常。

  

为什么Current会返回设置的元素,即使枚举数已经失效?

因为Current的值存储在迭代器中。虽然有一个很好的理由阻止你在修改集合时进一步迭代,但是可以让你在迭代器有效的位置访问该位置的值。

  

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

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

答案 1 :(得分:3)

  1. 每次从列表中添加/删除项目时都不会重新分配内部数组,这也不是枚举器失效的原因。这是一个更好的理由:Collection was modified; enumeration operation may not execute - why?

    简而言之,内部阵列在其容量耗尽时会重新分配,并且阵列的大小将加倍。某些集合还允许您修剪内部数组,例如List<T>.TrimExcess

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

  3. 如果您想了解更多信息,请查看source code,特别是EnsureCapacityList<T>.Enumerator

答案 2 :(得分:2)

  

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

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

回答你的问题:

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

  4. 1:It is not always true,实施中存在错误。