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

时间:2013-10-28 19:33:04

标签: c# ienumerable yield-return

考虑IEnumerator.Current的文档:

  

如果最后一次调用MoveNext,则当前也会抛出异常   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,不会抛出任何异常。查看compiler transformationCurrent只是一个字段支持的属性,它始终返回字段的值,仅此而已。也许它是某种形式的优化?不过,这似乎违反了合同。

1 个答案:

答案 0 :(得分:4)

虽然您对IEnumerator.Current的文档是正确的,但IEnumerator<T>.Current's documentation声明该属性未针对此类情况定义。使用迭代器,它返回“2”。 List<T>的枚举器返回default(T),而T[]会抛出异常。这些都是有效的实现,因为它是未定义的。

  

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

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

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

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

(为了进行比较,List<T>正确执行:如果在结束后获得IEnumerator.Current,则会抛出异常,如果在结束后调用IEnumerator<T>.Current,则返回{{ 1}})