working pattern of yield return

前端 未结 4 1427
一整个雨季
一整个雨季 2021-02-07 22:43

When i have a code block

static void Main()
{

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


        
4条回答
  •  小鲜肉
    小鲜肉 (楼主)
    2021-02-07 22:47

    Nope - far from it; I'll write a long-hand version for you... it is too grungy!


    Note it also helps if you understand that the foreach is actually:

    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 SupplyIntegers()
         {
             return new YieldEnumerable();
           }
        class YieldEnumerable : IEnumerable
        {
            public IEnumerator GetEnumerator()
            {
                return new YieldIterator();
            }
            IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
        }
        class YieldIterator : IEnumerator
        {
            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;
                }
            }
        }
    }
    

    As you can see, it builds a state machine in the iterator, with the state machine progressed by MoveNext. I've used the pattern with a state field, as you can see how this would work for more complex iterators.

    Importantly:

    • any variables in your iterator block become fields on the state machine
    • if you have a finally block (including using), it goes in the Dispose()
    • portions of code leading to a yield return become a case (roughly)
    • yield break becomes a state = -1; return false; (or similar)

    The way the C# compiler does this is very complicated, but it makes writing iterators a breeze.

提交回复
热议问题