如果原始集合修改了.NET,枚举器会发生什么

时间:2011-05-30 08:02:55

标签: .net generics collections

我有一个通用的Tree类,它将实现ICollection< T> (因此IEnumerable< T>和IEnumerable)。

为此,我必须实现一个TreeEnumerator类。

对Tree.GetEnumerator()的每次调用都将返回TreeEnumerator的新实例。

我有两个问题:

  1. 如果有许多TreeEnumerator对象浮动并且底层树发生了变化,那么会发生什么?如何处理?

  2. 在创建TreeEnumerator时,或者使用每个MoveNext执行一次遍历步骤,是否更好地在CopyToArray中执行所有树元素(在TreeEnumerator内部以便于数组遍历)?

  3. 我知道CopyToArray很容易一次遍历,但是空间成本很高。

    编辑

    了解版本机制后:

    您能指出此版本控制机制的示例代码吗?必须有标准命名和访问方式,因为foreach循环将需要对每个MoveNext进行此检查

3 个答案:

答案 0 :(得分:1)

在框架中的集合中处理它的方式,例如List<T>类,是保留列表状态的版本号。无论何时以任何方式更改列表的内容,版本号都会增加。

枚举器包含版本号的副本,以便在需要访问列表时检查列表是否保持不变。

在创建枚举器时复制项目将避免需要版本计数器,但这也会使创建枚举器变得昂贵。对于大多数集合,创建枚举器并不昂贵,因此如果可能,您应该尝试遵循该行为。

答案 1 :(得分:1)

通常,对底层集合的结构更改会使任何现有迭代器无效。这可以使用集合中的“版本号”来实现,可以在每个迭代步骤中进行检查。

例如,来自List<T>.GetEnumerator()的文档:

  

只要集合保持不变,枚举器仍然有效。如果对集合进行了更改,例如添加,修改或删除元素,则枚举数将无法恢复,并且其行为未定义。

(实际上,它会抛出InvalidOperationException。)

请注意,.NET 4中的并发集合明确允许在不使使迭代器无效的情况下更改集合。通常,迭代器只会看到原始元素,就像在调用GetEnumerator()时拍摄快照一样。

答案 2 :(得分:1)

这实际上取决于实施。如果您在枚举期间更改集合,大多数实现都不会爱你 - 并会故意抛出异常。但是,有可能(主要用于仅附加集合,但可能是所有集合)编写一个安全的枚举器(例如,通过枚举创建它时存在 - 但可以进行多种设计)。

就个人而言,我会避免这种情况;最好设计一下,这样你就可以枚举变异,但绝不会两者兼而有之。

如果你不能使用支持并发枚举和变异的集合,那么复制是可行的,但是再次 - 不是我的首选选项。