迭代器块的编译器实现不正确

本文关键字:实现 不正确 编译器 迭代器 | 更新日期: 2023-09-27 18:37:22

请考虑IEnumerator.Current的文档:

如果对 MoveNext 的最后一次调用返回,则 Current 也会引发异常 false,表示集合结束

但是,迭代器块不会发生这种情况。例如:

void Main()
{
    using (var enumerator = GetCounter().GetEnumerator())
    {
        for (int i = 0; i < 10; i++)
        {
            enumerator.MoveNext();
            Console.WriteLine (enumerator.Current);
        }
    }
}
static IEnumerable<int> GetCounter()
{
   for (int count = 0; count < 3; count++)
   {
       yield return count;
   }
}

将简单地打印 8 次2,没有抛出任何异常。查看编译器转换,Current只是一个字段支持的属性,它始终返回字段的值,仅此而已。也许是某种形式的优化?不过,这看起来像是违反了合同。

迭代器块的编译器实现不正确

虽然你对IEnumerator.Current的文档是正确的,但IEnumerator<T>.Current的文档指出,对于这种情况,该属性是未定义的。使用迭代器,它返回"2"。 List<T> 的枚举器返回 default(T)T[] 的抛出异常。这些都是有效的实现,因为它是未定义的。

在以下任一条件下未定义电流:

    枚举器
  • 位于集合中的第一个元素之前,紧接在创建枚举器之后。在读取 Current 的值之前,必须调用 MoveNext 以将枚举器前进到集合的第一个元素。
  • 对 MoveNext 的最后一次调用返回 false,表示集合结束。
  • 枚举器由于在集合中所做的更改(如添加、修改或删除元素)而失效。

值得注意的是,即使它实现了接口,从yield return生成的代码也没有正确实现IEnumerator,因为在这种情况下它继续返回2

IEnumerator enumerator = GetCounter().GetEnumerator();
for (int i = 0; i < 10; i++)
{
    enumerator.MoveNext();
    Console.WriteLine (enumerator.Current);
}

(为了比较,List<T>做对了:如果你在结束之后得到IEnumerator.Current,它会抛出一个异常,如果你在结束之后调用IEnumerator<T>.Current,它会返回default(T)