我通过调用得到一个枚举器IEnumerable<T>.GetEnumerator()
,然后调用它MoveNext()
直到它返回false,然后访问它的Current
属性。令我惊讶的是,没有抛出异常。
挖掘MSDN,我发现非泛型版本如果在返回false后访问会抛出Current
MoveNext()
,而泛型版本不会。
有人可以解释这种区别吗?
我通过调用得到一个枚举器IEnumerable<T>.GetEnumerator()
,然后调用它MoveNext()
直到它返回false,然后访问它的Current
属性。令我惊讶的是,没有抛出异常。
挖掘MSDN,我发现非泛型版本如果在返回false后访问会抛出Current
MoveNext()
,而泛型版本不会。
有人可以解释这种区别吗?
事实上,这种好奇心已经被多次注意到了——但最终,在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;
}
通用枚举器的行为是undefined,一切皆有可能,最终由集合类型来定义undefined的含义。
但是除了抛出之外,它们还可以做一些合理的事情,通用枚举器知道集合对象的类型。这样他们就可以回来了default(T)
。
非泛型枚举器没有那么奢侈,它们只能返回 null 或new object()
. 实际上,ArrayList 的代码为此目的保留了一个静态对象。但实际上并没有使用它,看起来他们在可用性测试后改变了主意。返回任何一个都会导致客户端代码失败,并出现非常令人不快的异常,NullReferenceException 或 InvalidCastException。在这些集合的正常使用中也可能引发异常,因此对于事故的实际原因几乎没有任何暗示。所以他们没有,而是抛出 InvalidOperationException。