【问题标题】:working pattern of yield return收益回报工作模式
【发布时间】:2010-12-08 11:40:09
【问题描述】:

当我有一个代码块时

static void Main()
{

  foreach (int i in YieldDemo.SupplyIntegers())
  {
    Console.WriteLine("{0} is consumed by foreach iteration", i);
  }
}


 class YieldDemo
  {
    public static IEnumerable<int> SupplyIntegers()
     {
         yield return 1;
         yield return 2;
          yield return 3;
       }
   }

我可以将收益回报背后的原理解释为

  1. Main() 调用 SupplyIntegers()
  2. |1| |2| |3| are stored in contiguous memory block.Pointer of "IEnumerator" Moves to |1|
  3. 控制从 SupplyInteger() 返回到 Main()。
  4. Main() 打印值
  5. 指针移至 |2|,依此类推。

澄清:

(1) 通常我们会在一个函数中允许一个有效的返回语句。如何做 当多个 yield return ,yield return,... 语句存在时,C# 处理?

(2) 一旦遇到返回就没有办法控制再次返回 SupplyIntegers(),如果允许,Yield 不会再次从 1 开始吗?我的意思是收益率回报1?

【问题讨论】:

  • 再次询问一本书:C# in Depth (Manning, Skeet),第 6 章。它是免费示例章节,涵盖迭代器块。它不完全是一本 C# 初学者书籍(远非如此),但您将很难找到关于该主题的更好参考。
  • 如果权限允许问“你没写过书吗?”
  • 不,我没有。我为出版商做一些校对,偶尔会写一些奇怪的文章等。但我没有书。
  • 如果你这样做我会很高兴,因为我已经看到你对一些问题的解释。它适合从初学者到资深程序员

标签: c# enumeration


【解决方案1】:

不——远非如此;我给你写个长手版……太邋遢了!


请注意,如果您了解 foreach 实际上是:

using(var iterator = YieldDemo.SupplyIntegers().GetEnumerator()) {
    int i;
    while(iterator.MoveNext()) {
        i = iterator.Current;
         Console.WriteLine("{0} is consumed by foreach iteration", i);
    }
}

using System;
using System.Collections;
using System.Collections.Generic;
static class Program
{
    static void Main()
    {

        foreach (int i in YieldDemo.SupplyIntegers())
        {
            Console.WriteLine("{0} is consumed by foreach iteration", i);
        }
    }
}

 class YieldDemo
  {

    public static IEnumerable<int> SupplyIntegers()
     {
         return new YieldEnumerable();
       }
    class YieldEnumerable : IEnumerable<int>
    {
        public IEnumerator<int> GetEnumerator()
        {
            return new YieldIterator();
        }
        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
    }
    class YieldIterator : IEnumerator<int>
    {
        private int state = 0;
        private int value;
        public int Current { get { return value; } }
        object IEnumerator.Current { get { return Current; } }
        void IEnumerator.Reset() { throw new NotSupportedException(); }
        void IDisposable.Dispose() { }
        public bool MoveNext()
        {
            switch (state)
            {
                case 0: value = 1; state = 1;  return true;
                case 1: value = 2; state = 2;  return true;
                case 2: value = 3; state = 3; return true;
                default: return false;
            }
        }
    }
}

如你所见,它在迭代器中构建了一个状态机,状态机前进了MoveNext。我已经将该模式与 state 字段一起使用,您可以看到这对于更复杂的迭代器是如何工作的。

重要的是:

  • 迭代器块中的任何变量都成为状态机上的字段
  • 如果你有一个finally 块(包括using),它进入Dispose()
  • 导致yield return 的部分代码变为case(大致)
  • yield break 变为 state = -1; return false;(或类似)

C# 编译器执行此操作的方式非常复杂,但它使编写迭代器变得轻而易举。

【讨论】:

  • 令人难以置信的是,人们在它实际上是一个有用的答案之前就已经支持了。
  • @Joren:这就是 Marc 的出色表现。 ;) 优秀的答案马克!
  • 这真的很简单,当 Marc 说他要发布一些东西时,你几乎可以打赌它会是正确的。
  • 无法抗拒的解释和无可挑剔的笔记。作为初学者,我从你们所有人那里吸取了足够多的东西。谢谢大家的关心。 :)
  • @Joren 是对的。我也不会投票给它。但是,如果我(例如)看到 Jon 有一个类似的“进行中”标记,我就不会花费很多精力来复制它; 是我的意图。
【解决方案2】:

它只是一个语法糖,.net 为您生成 IEnumerator 类并实现 MoveNext、Current 和 Reset 方法,而不是生成返回该 IEnumerator 的 IEnumarable 类 GetEnumerator,您可以通过 .net 反射器或 ildasm 看到魔术类。

另见here

【讨论】:

    【解决方案3】:

    简单地说,迭代器块(或带有yield 语句的方法,如果可能的话)被编译器转换为编译器生成的类。该类实现了IEnumerator,并且yield 语句被转换为该类的“状态”。

    例如,这个:

    yield return 1;
    yield return 2;
    yield return 3;
    

    可能会变成类似的东西:

    switch (state)
    {
        case 0: goto LABEL_A;
        case 1: goto LABEL_B;
        case 2: goto LABEL_C;
    }
    LABEL_A:
        return 1;
    LABEL_B:
        return 2;
    LABEL_C:
        return 3;
    

    迭代器块可以看作是抽象的状态机。此代码将由IEnumerator 的方法调用。

    【讨论】:

      【解决方案4】:

      简而言之,当编译器看到 yield 语句时(当你在等待 marc 的长手版本时),它会在后台为您构建一个自定义类的新实例,该实例实现了一个名为 IEnumerator 的接口,该接口具有方法Current()MoveNext(),并跟踪您当前在迭代过程中的位置...在上面的示例中,它还将跟踪要枚举的列表中的值。

      【讨论】:

        猜你喜欢
        • 2011-05-27
        • 1970-01-01
        • 2012-01-14
        • 1970-01-01
        • 2018-01-23
        • 2010-12-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-23
        相关资源
        最近更新 更多