我有一个通用的Tree类,它将实现ICollection< T> (因此IEnumerable< T>和IEnumerable)。
为此,我必须实现一个TreeEnumerator类。
对Tree.GetEnumerator()的每次调用都将返回TreeEnumerator的新实例。
我有两个问题:
如果有许多TreeEnumerator对象浮动并且底层树发生了变化,那么会发生什么?如何处理?
在创建TreeEnumerator时,或者使用每个MoveNext执行一次遍历步骤,是否更好地在CopyToArray中执行所有树元素(在TreeEnumerator内部以便于数组遍历)?
我知道CopyToArray很容易一次遍历,但是空间成本很高。
编辑:
了解版本机制后:
您能指出此版本控制机制的示例代码吗?必须有标准命名和访问方式,因为foreach循环将需要对每个MoveNext进行此检查
答案 0 :(得分:1)
在框架中的集合中处理它的方式,例如List<T>
类,是保留列表状态的版本号。无论何时以任何方式更改列表的内容,版本号都会增加。
枚举器包含版本号的副本,以便在需要访问列表时检查列表是否保持不变。
在创建枚举器时复制项目将避免需要版本计数器,但这也会使创建枚举器变得昂贵。对于大多数集合,创建枚举器并不昂贵,因此如果可能,您应该尝试遵循该行为。
答案 1 :(得分:1)
通常,对底层集合的结构更改会使任何现有迭代器无效。这可以使用集合中的“版本号”来实现,可以在每个迭代步骤中进行检查。
例如,来自List<T>.GetEnumerator()
的文档:
只要集合保持不变,枚举器仍然有效。如果对集合进行了更改,例如添加,修改或删除元素,则枚举数将无法恢复,并且其行为未定义。
(实际上,它会抛出InvalidOperationException
。)
请注意,.NET 4中的并发集合明确允许在不使使迭代器无效的情况下更改集合。通常,迭代器只会看到原始元素,就像在调用GetEnumerator()
时拍摄快照一样。
答案 2 :(得分:1)
这实际上取决于实施。如果您在枚举期间更改集合,大多数实现都不会爱你 - 并会故意抛出异常。但是,有可能(主要用于仅附加集合,但可能是所有集合)编写一个安全的枚举器(例如,通过枚举创建它时存在 - 但可以进行多种设计)。
就个人而言,我会避免这种情况;最好设计一下,这样你就可以枚举或变异,但绝不会两者兼而有之。
如果你不能使用支持并发枚举和变异的集合,那么复制是可行的,但是再次 - 不是我的首选选项。