C# 为什么IEnumerator.Current的错误处理与IEnumerator不同<;T>;。现在的
我本以为为实现C# 为什么IEnumerator.Current的错误处理与IEnumerator不同<;T>;。现在的,c#,ienumerable,C#,Ienumerable,我本以为为实现IEnumerable的空集合执行以下代码会引发异常: var enumerator = collection.GetEnumerator(); enumerator.MoveNext(); var type = enumerator.Current.GetType(); // Surely should throw? 因为集合是空的,所以访问IEnumerator.Current是无效的,我希望出现异常。但是,列表不会引发异常 这是允许的,表示在以下任何条件下,Current未
IEnumerable
的空集合执行以下代码会引发异常:
var enumerator = collection.GetEnumerator();
enumerator.MoveNext();
var type = enumerator.Current.GetType(); // Surely should throw?
因为集合是空的,所以访问IEnumerator.Current
是无效的,我希望出现异常。但是,列表
不会引发异常
这是允许的,表示在以下任何条件下,Current
未定义:
- 枚举数位于集合中第一个元素之前,紧随创建枚举数之后。在读取Current的值之前,必须调用MoveNext将枚举数前进到集合的第一个元素
- 对MoveNext的最后一次调用返回false,这表示 收藏
- 由于在集合中所做的更改(例如添加、修改或删除元素),枚举数无效
IEnumerable
,则会出现异常。该行为由指定,其中规定:
- 如果对MoveNext的最后一次调用返回false(表示集合结束),Current应引发InvalidOperationException
IEnumerable
还是IEnumerable
,如下程序所示(请注意showElementType1()
和showElementType1()
中的代码是如何相同的):
使用系统;
使用系统集合;
使用System.Collections.Generic;
命名空间控制台应用程序2
{
班级计划
{
公共静态void Main()
{
var list=新列表();
showElementType1(列表);//不引发异常。
showElementType2(列表);//引发异常。
}
私有静态void showElementType1(IEnumerable集合)
{
var枚举器=collection.GetEnumerator();
枚举数。MoveNext();
var type=enumerator.Current.GetType();//此处未引发异常。
控制台写入线(类型);
}
私有静态void showElementType2(IEnumerable集合)
{
var枚举器=collection.GetEnumerator();
枚举数。MoveNext();
var type=enumerator.Current.GetType();//此处引发InvalidOperationException。
控制台写入线(类型);
}
}
}
IEnumerable的问题在于当前的类型为T
。而不是抛出异常(它是从MoveNextRare
设置的)
使用IEnumerable
时,您没有该类型,并且无法返回默认值
实际问题是您没有检查MoveNext
的返回值。如果返回false
,则不应调用Current
。例外情况是可以的。我认为他们发现在IEnumerable
案例中返回default(T)
更方便
异常处理会带来开销,返回的default(T)
没有(那么多)。可能他们只是认为在IEnumerable
的情况下,从Current
属性返回没有什么用处(他们不知道类型)。使用default(T)
时,该问题在IEnumerable
中“解决”
据此(感谢评论):
出于性能原因,生成的枚举数的Current属性保持非常简单—它只返回生成的“Current”支持字段的值
这可能指向异常处理开销的方向。或验证当前值所需的额外步骤
他们实际上只是将责任推给了foreach
,因为这是枚举器的主要用户:
绝大多数与枚举数的交互都是以foreach循环的形式进行的,foreach循环已经防止访问这些状态中的任何一种状态的电流,因此在每次迭代中消耗额外的CPU周期来检查几乎没有人会遇到的这些状态是浪费的
更好地匹配人们在实践中如何实现它。同样,将以前版本的文档中的“当前也引发异常…”更改为当前版本中的“当前应引发…”
根据实现的工作方式,抛出异常可能是一项相当大的工作,但是由于Current
与MoveNext()
结合使用,因此很少会出现异常状态。当我们认为绝大多数的使用都是编译器生成的,并且实际上没有bug的范围,在代码< > MOVENTXEL()/代码>之前,或者在它返回了代码> false < /代码>之后,调用一个bug。在正常使用情况下,我们可以预期这种情况永远不会出现
因此,如果您正在编写IEnumerable
或IEnumerable
的实现,而捕获错误条件非常困难,您可能会决定不这样做。如果你做了这个决定,它可能不会给你带来任何问题。是的,你违反了规则,但可能没关系
而且,由于它不会导致任何问题,除了以错误的方式使用接口的人之外,因此将其记录为未定义的行为会将负担从实现者转移到调用者身上,使其不做调用者应该做的事情
using System;
using System.Collections;
using System.Collections.Generic;
namespace ConsoleApplication2
{
class Program
{
public static void Main()
{
var list = new List<int>();
showElementType1(list); // Does not throw an exception.
showElementType2(list); // Throws an exception.
}
private static void showElementType1(IEnumerable<int> collection)
{
var enumerator = collection.GetEnumerator();
enumerator.MoveNext();
var type = enumerator.Current.GetType(); // No exception thrown here.
Console.WriteLine(type);
}
private static void showElementType2(IEnumerable collection)
{
var enumerator = collection.GetEnumerator();
enumerator.MoveNext();
var type = enumerator.Current.GetType(); // InvalidOperationException thrown here.
Console.WriteLine(type);
}
}
}