IEnumerator<T>.当前超出 MoveNext() == false

Geo*_*voy 1 .net c# iterator

我的图形库中有以下代码:

while(true) {
    using (var i = ancestry.GetAdjacent(current).GetEnumerator())
    {
        if (!i.MoveNext())
            yield break;

        if (i.MoveNext())
            throw new InvalidOperationException("ancestry graph can only have one adjacent vertex for any given vertex");
        // it's a bug, as some enumerators could return null after failed MoveNext().
        current = i.Current;
        if (isSentinel(current))
            yield break;
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码适用于 IEnumerator 的某些实例,但不适用于其他实例(.NET 的所有标准实现,以及具有产量返回的迭代器方法)。

这种行为是否未指定?

如果不是,正确的表示法是什么?将“当前”保留为上一个,还是设置为默认值(T)?

更新:

我探索了 IEnumerator 的一些集合实现、Linq 扩展以及类型化数组:fiddle

根据我的理解,Linq 违反了 IEnumerable 的约定,因为它不会在枚举结束后抛出 Current ,而类型化数组也违反了 IEnumerable 的约定,因为它抛出 on IEnumerable<T>.Current,并且没有为 IEnumerable.Current 指定异常类型。

有人可以澄清一下,文档中所说的“未定义”是什么意思?是否包括抛出未指定的异常:

来自IEnumerator.Current 属性文档:

在以下任何条件下,Current 都是未定义的: 在创建枚举数之后,枚举数立即定位在集合中的第一个元素之前。在读取 Current 的值之前,必须调用 MoveNext 将枚举器前进到集合的第一个元素。最后一次调用 MoveNext 返回 false,这表明集合结束。由于集合中发生的更改(例如添加、修改或删除元素),枚举数会失效。Current 返回相同的对象,直到调用 MoveNext。MoveNext 将 Current 设置为下一个元素。

Ser*_*rvy 6

如果按照接口的约定返回,则的值Current未定义,如其文档中所述。你永远不应该依赖任何特定的行为。您根本不应该检查每当is的值。MoveNextfalseCurrentMoveNextfalse

如果MoveNext传递到集合末尾,则枚举数位于集合中最后一个元素之后并MoveNext返回false。当枚举器位于此位置时,后续调用MoveNext也会返回false。如果最后一次调用MoveNextreturned false,Current未定义。

(请注意,IEnumeratorCurrent这种情况下应该抛出异常,但根据我的经验,大多数实现都不会显式抛出异常,它们只是公开未定义的值;此更改反映在较新IEnumerator<T>接口的文档中。)

  • @Ama 这意味着你不知道这意味着什么。您无法知道它是否会返回 null、随机值、抛出异常或*实际上是其他任何内容*。这就是未定义的定义。因此,当提供您自己的实现时,*无论您做什么,您都满足了合同*。 (3认同)