【问题标题】:Why the Reset() method on Enumerator class must throw a NotSupportedException()?为什么 Enumerator 类的 Reset() 方法必须抛出 NotSupportedException()?
【发布时间】:2009-09-23 19:43:22
【问题描述】:

根据我在http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx 上看到的内容以及 Jon Skeet 的文章,c# 规范本身就是这么说的。会是什么原因?

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    这不是我阅读C# spec [Word doc] 的方式。第 10.14.4 节“枚举器对象”,状态:

    ...[E]numerator 对象不支持 IEnumerator.Reset 方法。调用此方法会引发 System.NotSupportedException。

    但是,这部分(和语句)特定于“枚举器对象”,其定义为:

    当使用迭代器块实现返回枚举器接口类型的函数成员时,调用函数成员不会立即执行迭代器块中的代码。而是创建并返回一个枚举器对象

    换句话说,“枚举器对象”是编译器生成的IEnumerator1。每个IEnumerator 都没有限制,只有从迭代器块(又名yield)生成的那些。

    至于为什么?我怀疑是因为在一般情况下这有点不可能 - 如果不保存每个值以及随之而来的内存限制。再加上IEnumerator.Reset() 很少使用(您最后一次重置枚举器是什么时候?)以及MSDN specifically calls out that it need not be implemented

    为 COM 互操作性提供了 Reset 方法。它不一定需要实施;相反,实现者可以简单地抛出 NotSupportedException。

    您可以在没有人真正注意到的情况下消除很多复杂性。

    至于要求它抛出2,我想这比让实现者决定要简单。 IMO,要求抛出有点多 - 编译器(或其他实现1)可能会生成一个重置方法的合理情况,但我不认为它是真实的也有问题。

    1 从技术上讲,该规范保留了其他实现的可能性:

    枚举器对象通常是编译器生成的枚举器类的实例,它封装迭代器块中的代码并实现枚举器接口,但其他实现方法也是可能的。

    但我不知道任何其他具体实现。无论如何,为了符合要求,“枚举器对象”的其他实现也必须抛出 NotSupportedException

    2 Nitpicker 的角落:我认为即使在“要求”中也可能存在一些小问题。该规范不使用首选的"MUST, SHOULD, MAY" 措辞,使其有点开放。我将“原因”更多地阅读为实施说明 - 不是要求。再说一次,我还没有阅读整个规范,所以也许他们对这些术语的定义更多或在其他地方更明确。

    【讨论】:

    • “为 COM 互操作性提供了 Reset 方法。”你能进一步解释一下吗?什么是 COM 互操作性,我们为什么要关心它?
    【解决方案2】:

    不可能在所有序列中都正确支持;许多只是一次(网络流等)。如果你不能一直依赖它,它就毫无用处,因为抽象被破坏了。当然你可以有一个IResettableEnumerator,但是IEnumerator 上的Reset() 没有 工作。坦率地说,这是一个错误(IMO)。

    我怀疑它也会使迭代器块甚至更加复杂(它们目前是编译器中最复杂的两个部分之一;我不记得哪个是“顶部”;它们,或匿名方法/捕获的变量)。

    【讨论】:

    • 嗯,迭代器块和匿名函数都很复杂,因为它们捕获局部变量并将它们提升到闭包类。更糟糕的是有点折腾。匿名函数嵌套,这使得它们变得复杂,而 lambdas 与类型系统有复杂的交互。迭代器有一个复杂的代码生成策略,包括创建“处理器”来处理 finally 块。那么,谁知道呢?他们都很难做到正确。
    • 这是一个设计错误的另一个原因是它是不必要的;如果要重置到开头,只需创建一个新的迭代器对象。迭代器很便宜;重复使用它们没有任何好处。为了使重置有用,我们真正更重要的是能够在特定点“快照”迭代器,以便您可以继续执行一个副本,然后“回到原来的位置”。
    • 我怀疑详细程度超过了需要独立回答的程度......但感谢您填写一些空白。
    • 如果我理解正确,IEnumerator.Reset 在 .NET 1.0 中存在的唯一原因是它可以包装像 IEnumVARIANT 这样的 COM 枚举器,它通常具有 Reset(这可能是优化)。
    • 它不一定是“损坏的”,只是可能将多个抽象组合成一个。看看 C++,有六个与迭代器相关的不同概念。 sgi.com/tech/stl/Iterators.html
    【解决方案3】:

    这是 MSDN 所说的

    为 COM 提供了 Reset 方法 互操作性。它不 必然需要实施; 相反,实施者可以简单地 抛出 NotSupportedException。

    http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset.aspx>MSDN IEnumerator..::.Reset 方法

    没有说必须,只是说可以。

    编辑: 然而,正如 Marc 所指出的,C# 2.0 规范存在差异

    22.2 枚举器对象

    请注意,枚举器对象不 支持 IEnumerator.Reset 方法。 调用此方法导致 System.NotSupportedException 为 扔了。

    【讨论】:

      猜你喜欢
      • 2014-06-02
      • 2016-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-04
      • 2012-09-18
      • 1970-01-01
      相关资源
      最近更新 更多