【问题标题】:What happens to enumerators if the original collection modified .NET如果原始集合修改了 .NET,枚举器会发生什么
【发布时间】:2011-05-30 08:02:54
【问题描述】:

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

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

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

我有两个问题:

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

  2. 最好在创建 TreeEnumerator 时对所有树元素(在 TreeEnumerator 内以方便数组遍历)执行 CopyToArray 还是对每个 MoveNext 执行一次遍历?

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

编辑

了解版本机制后:

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

【问题讨论】:

    标签: .net generics collections


    【解决方案1】:

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

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

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

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

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

    【讨论】:

    • @Jon 班级是否需要跟踪版本号?基本上如何支持失效?
    • @Munish:集合有一个版本号,它会更新,迭代器知道创建迭代器时集合的版本号。
    • @Jon 我认为我在评论中的问题不清楚。那么这个版本号对程序员隐藏了吗?我的意思是它是自动支持的还是我需要在修改时更新版本并在迭代时检查版本等。
    • @Munish:版本号对类的 user 是隐藏的,但是如果你要实现一个集合,你需要把自己在那个机器里。
    • @Munish:不,foreach 不这样做 - 它在 MoveNext 方法中。请注意,您很少需要从编写自己的集合类开始。
    【解决方案2】:

    在框架中的集合(例如List<T> 类)中处理此问题的方式是为列表的状态保留一个版本号。每当列表的内容以任何方式发生变化时,版本号都会增加。

    枚举器包含版本号的副本,以便它可以在需要访问列表时检查列表是否未更改。

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

    【讨论】:

      【解决方案3】:

      这真的取决于实施。如果您在枚举期间更改集合,大多数实现都不会喜欢您 - 并且会故意抛出异常。然而,编写一个安全的枚举器是可能的(主要用于仅附加的集合,但对所有人来说都是可能的)(例如,通过枚举创建时存在的所有值 - 但可能有多种设计)。

      就个人而言,我会避免这种情况;最好将它设计成枚举 变异,但不要同时枚举。

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

      【讨论】:

      • 我也喜欢快照的想法。但是很难为树状集合保留快照,因为子节点指针可能已更改。但是,如果我只是将节点保留在快照枚举器中(不验证它们的子/父指针语义),那么它会起作用。 .NET 4 中是否有任何并发​​收集的示例,它可以最好地同时处理突变和枚举,并且副作用最小?
      • @Munish - Jon 列出的并发那些应该会有所帮助。为了我自己的需要,我只是写了我自己的(我有一些非常具体的要求,这并不是完全疯狂......)
      猜你喜欢
      • 2012-04-30
      • 1970-01-01
      • 2012-07-30
      • 1970-01-01
      • 2014-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-22
      相关资源
      最近更新 更多