【问题标题】:Incorrect compiler implementation of iterator blocks?迭代器块的编译器实现不正确?
【发布时间】:2013-10-28 19:33:04
【问题描述】:

考虑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,不会抛出异常。查看compiler transformationCurrent 只是一个字段支持的属性,它总是返回字段的值,仅此而已。也许这是某种形式的优化?尽管如此,这看起来还是违反了合同。

【问题讨论】:

  • 哦,我错过了!显然它已更改为“未定义”。我想这回答了我的问题,但我不能接受评论,所以请给出答案。
  • 你可以回答你自己的问题。

标签: c# ienumerable yield-return


【解决方案1】:

虽然您对IEnumerator.Current 的文档是正确的,但IEnumerator&lt;T&gt;.Current's documentation 声明该属性对于这种情况是未定义的。使用您的迭代器,它返回“2”。 List&lt;T&gt; 的枚举器返回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&lt;T&gt; 做对了:如果在结束后得到IEnumerator.Current,则抛出异常,如果在结束后调用IEnumerator&lt;T&gt;.Current,则返回default(T)

【讨论】:

  • +1 但我们又回到了广场,不是吗? IEnumerator 没有正确实现,也许我应该在连接中提交一个错误?
  • @OhadSchneider 是的,你是对的。当然,这是一个小错误,但它是一个错误。
  • 好吧,我在这里提交了一个错误:connect.microsoft.com/VisualStudio/feedback/details/806924/…
猜你喜欢
  • 2010-11-10
  • 2012-03-03
  • 1970-01-01
  • 2021-11-15
  • 2011-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-09
相关资源
最近更新 更多