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

18
3个回答

13

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

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


3
迭代器块和匿名函数都很复杂,因为它们会捕获本地变量并将其提升到闭包类中。哪个更糟糕有点难以抉择。匿名函数嵌套,使它们复杂,而Lambda与类型系统有复杂的交互。迭代器具有复杂的代码生成策略,涉及创建“处理程序”来处理最终块。所以谁知道呢?它们都很麻烦,需要正确地处理。 - Eric Lippert
6
这是设计上的错误之一,原因在于它是不必要的。如果你想回到开头,只需要创建一个全新的迭代器对象即可。迭代器很便宜,重复使用它们没有任何好处。为了让重置有用,我们真正需要的是"快照"功能,即能够在特定点上截取迭代器,这样你就可以在一个副本上前进,然后"返回到之前的位置"。 - Eric Lippert
1
我怀疑这个细节程度已经超出了需要一个独立回答的范畴...但是感谢您填补了一些空白。 - Marc Gravell
1
如果我理解正确,.NET 1.0 中 IEnumerator.Reset 存在的唯一原因是为了包装 COM 枚举器,例如 IEnumVARIANT,它通常具有 Reset(并且可能已经优化)。 - Pavel Minaev
它不一定是“损坏”的,只可能是多个抽象合并成一个。看看C++,有六个与迭代器相关的概念。http://www.sgi.com/tech/stl/Iterators.html - cdiggins

13
我阅读C# spec [Word doc]时,并不认为这里说的是所有的IEnumerator都不支持IEnumerator.Reset()方法。在第10.14.4节“枚举器对象”中指出:

…[枚举器对象]不支持IEnumerator.Reset()方法,调用此方法会引发System.NotSupportedException异常。

但是,该部分(和说明)特指“enumerator objects”,其定义为:

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

换句话说,“enumerator object” 是由编译器生成的 IEnumerator1。对于每个 IEnumerator 并没有限制,仅对从迭代块(称为 yield)生成的那些有限制。

至于为什么?我猜测因为在一般情况下这几乎是不可能做到的--没有保存每个值及其带来的内存限制。再加上IEnumerator.Reset()很少使用(你上次Reset一个enumerator是什么时候?),而且MSDN专门指出它不需要被实现

Reset方法是为COM互操作性提供的。 不一定需要实现它; 相反,实现者可以只是抛出NotSupportedException异常。

这样做可以减少很多复杂性而不会引起任何人的注意。

关于要求它抛出异常,我认为这只是比让实现者决定更简单的方法。在我看来,要求抛出异常有点过分了 - 可能存在一些合理情况,编译器(或其他实现1)可以为其生成Reset方法,但我并不认为这是一个真正的问题。

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

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

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

2 吹毛求疵的角落:我认为即使是“要求”抛出异常,也可能存在一些争议。规范在不使用首选“MUST,SHOULD,MAY”措辞的情况下,留下了一些余地。我认为“cause”更多的是一种实现的说明 - 而不是一项要求。不过,我没有阅读整个规范,因此也许他们在其他地方对这些术语进行了更明确的定义或更详细的说明。


"Reset方法是为了支持COM互操作性而提供的。" 您能进一步解释一下吗?什么是COM互操作性,我们为什么要关心它? - David Klempfner

4
这里是MSDN的内容:
Reset方法是为了实现COM互操作而提供的。它不一定需要被实现;相反,实现者可以简单地抛出NotSupportedException。
https://msdn.microsoft.com/zh-cn/library/system.collections.ienumerator.reset.aspx>MSDN IEnumerator..::.Reset Method
它并没有说必须,只是说可以。
编辑: 然而,正如Marc所指出的那样,在C# 2.0规范中有所区别。
22.2 枚举器对象 请注意,枚举器对象不支持IEnumerator.Reset方法。调用此方法会导致System.NotSupportedException异常被抛出。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接