为什么Enumerator类上的Reset()方法必须抛出NotSupportedException()?

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

这不是我读C#规范 [Word doc]的方式。 第10.14.4节“枚举器对象”,指出:

… [E]分子对象不支持IEnumerator.Reset方法。 调用此方法会导致抛出System.NotSupportedException。

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

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

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

至于为什么? 我怀疑是因为在一般情况下这是不可能做到的 – 没有保存每个值以及随之而来的内存限制。 将它与很少使用IEnumerator.Reset()的事实相结合(当你最后一次重置枚举器的时候?)并且MSDN特别指出它不需要实现 :

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

你可以在没有人注意的情况下削减很多复杂性。

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

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

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

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

2 Nitpicker的角落:我认为即使在“要求”投掷也可能有些狡辩。 规范,在没有使用首选的“MUST,SHOULD,MAY”的措辞,让它有点开放。 我更多地将“原因”作为实施说明 – 而不是要求。 然后,我还没有阅读整个规范,所以也许他们在其他地方更多或更明确地定义这些术语。

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

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

这是MSDN所说的

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

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

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

编辑:然而正如Marc指出的那样,C#2.0 Spec有所不同

22.2枚举器对象

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