5

我通过调用得到一个枚举器IEnumerable<T>.GetEnumerator(),然后调用它MoveNext()直到它返回false,然后访问它的Current属性。令我惊讶的是,没有抛出异常。

挖掘MSDN,我发现非泛型版本如果在返回false后访问会抛出CurrentMoveNext(),而泛型版本不会

有人可以解释这种区别吗?

4

2 回答 2

2

事实上,这种好奇心已经被多次注意到了——但最终,在MoveNext()返回之后false,您应该考虑Current未定义行为的价值。如果你这样做:这里没有问题。只是:不要Current在这里访问- 那么它是“最后一个已知值”还是“该类型的默认值”还是“抛出异常”都没有关系。

另外,请注意,由于异常是由implementation引发的,因此泛型 API 的实现也完全有可能抛出,而非泛型 API 则不抛出。作为后者的一个例子 - 迭代器块不要在这里抛出

    static void Main()
    {
        IEnumerator<int> typed = GetInts();
        typed.MoveNext();
        Console.WriteLine(typed.MoveNext());
        int i = typed.Current;

        IEnumerator untyped = GetInts();
        untyped.MoveNext();
        Console.WriteLine(untyped.MoveNext());
        object o = untyped.Current;
    }

    static IEnumerator<int> GetInts()
    {
        yield return 4;
        yield break;
    }
于 2013-10-21T08:28:44.753 回答
2

通用枚举器的行为是undefined,一切皆有可能,最终由集合类型来定义undefined的含义。

但是除了抛出之外,它们还可以做一些合理的事情,通用枚举器知道集合对象的类型。这样他们就可以回来了default(T)

非泛型枚举器没有那么奢侈,它们只能返回 null 或new object(). 实际上,ArrayList 的代码为此目的保留了一个静态对象。但实际上并没有使用它,看起来他们在可用性测试后改变了主意。返回任何一个都会导致客户端代码失败,并出现非常令人不快的异常,NullReferenceException 或 InvalidCastException。在这些集合的正常使用中也可能引发异常,因此对于事故的实际原因几乎没有任何暗示。所以他们没有,而是抛出 InvalidOperationException。

于 2013-10-21T10:32:23.797 回答