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

如果原始集合修改了.NET,枚举数会发生什么情况,.net,generics,collections,.net,Generics,Collections,我有一个通用树类,它将实现ICollection(因此IEnumerable和IEnumerable) 为此,我必须实现一个TreeEnumerator类 对Tree.GetEnumerator()的每次调用都将返回TreeEnumerator的一个新实例 我有两个问题: 如果有许多TreeEnumerator对象四处浮动,并且基础树发生了变化,那么会发生什么?怎么处理 在创建TreeEnumerator时,最好是执行CopyToArray(在TreeEnumerator内部以便于数组遍历),还

我有一个通用树类,它将实现ICollection(因此IEnumerable和IEnumerable)

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

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

我有两个问题:

  • 如果有许多TreeEnumerator对象四处浮动,并且基础树发生了变化,那么会发生什么?怎么处理

  • 在创建TreeEnumerator时,最好是执行CopyToArray(在TreeEnumerator内部以便于数组遍历),还是在每次移动下一步时执行一步遍历

  • 我知道CopyToArray很容易一次遍历,但会占用空间

    编辑

    了解版本机制后:


    您能指出这个版本控制机制的示例代码吗?必须有标准的命名和访问方式,因为foreach循环需要对每个MoveNext进行检查。在框架的集合中,例如
    列表
    类,处理这种情况的方式是为列表的状态保留一个版本号。每当列表的内容以任何方式更改时,版本号都会增加

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


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

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

    例如,从以下文档:

    只要集合保持不变,枚举数就保持有效。如果对集合进行了更改,例如添加、修改或删除元素,则枚举数将不可恢复地失效,其行为也未定义

    (实际上,它将抛出
    InvalidOperationException


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

    这实际上取决于实现。如果在枚举期间更改集合,大多数实现都不会喜欢您,并且会故意抛出异常。但是,可以编写安全的枚举器(主要针对仅追加集合,但也可能针对所有集合)(例如,通过在创建枚举器时尽可能多地枚举它,但也可以进行多个设计)

    就我个人而言,我会避免这种情况;最好将其设计为枚举或变异,但决不能两者兼而有之


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

    @Jon类是否需要跟踪版本号?基本上如何支持无效?@Munish:集合有一个它更新的版本号,迭代器知道创建迭代器时集合所在的版本号。@Jon我想我在评论中的问题不清楚。那么这个版本号对程序员来说是隐藏的吗?我的意思是,它是自动支持的,还是我需要在修改时更新版本,并在迭代时检查版本等@Munish:版本号对类的用户是隐藏的,但如果您正在实现一个集合,您需要自己放入该机器中。@Munish:不,foreach不这样做-它在MoveNext方法中。请注意,您很少需要编写自己的集合类作为开始。我也喜欢快照的想法。但很难为树型集合保留快照,因为子节点指针可能已更改。但是,如果我只保留快照枚举器中的节点(而不验证它们的子/父指针语义),那么它就可以工作了。在.NET4中,有没有并发收集的例子可以最好地同时处理变异和枚举,并且副作用最少?@Munish-Jon列出的并发收集应该会有所帮助。为了我自己的需要,我只写了我自己的(我有一些非常具体的要求,这并不完全疯狂…)