收益率的工作模式

时间:2009-10-20 19:42:09

标签: c# enumeration

当我有代码块时

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 |,依此类推。
  6. 澄清:

    (1)通常我们将在函数内部允许一个有效的return语句。如何     C#处理多个yield return,yield return,...语句是否存在?

    (2)一旦遇到回报,再也无法再进行控制     SupplyIntegers(),如果允许则不会再从1开始收益率?我的意思是收益率1?

4 个答案:

答案 0 :(得分:33)

不 - 远离它;我会为你写一个长手版......它太蹩脚了!


请注意,如果您了解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#编译器执行此操作的方式非常复杂,但它使编写迭代器变得轻而易举。

答案 1 :(得分:3)

它只是一个语法糖,.net为你生成IEnumerator类并实现MoveNext,Current和Reset方法,而不是生成IEnumarable类GetEnumerator,它返回IEnumerator,你可以通过.net reflector或ildasm看到魔术类。 / p>

另见here

答案 2 :(得分:2)

简单地说,迭代器块(或具有yield语句的方法,如果可能)由编译器转换为编译器生成的类。该类实现IEnumeratoryield语句转换为该类的“状态”。

例如,这个:

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的方法调用。

答案 3 :(得分:1)

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