C#: How to translate the Yield Keyword

  • What would the MSDN sample look like without the yield keyword? You may use any example if you perfer. I would just like to understand what is going on under the hood.
  • Is the yield operator eagerly or lazily evaluated?
  • Sample:

    using System;
    using System.Collections;
    public class List
    {
        public static IEnumerable Power(int number, int exponent)
        {
            int counter = 0;
            int result = 1;
            while (counter++ < exponent)
            {
                result = result * number;
                yield return result;
            }
        }
    
        static void Main()
        {
            // Display powers of 2 up to the exponent 8:
            foreach (int i in Power(2, 8))
            {
                Console.Write("{0} ", i);
            }
        }
    }
    
  • MSDN - Yield Keyword

  • If the yield operator is eagerly evaluated here is my guess:

        public static IEnumerable Power(int number, int exponent)
        {
            int counter = 0;
            int result = 1;
            List<int> powers;
            while (counter++ < exponent)
            {
                result = result * number;
                powers.add(result);
            }
            return powers;
        }
    

    I have no clue what it might look like if the yield operator is lazily evaluated.

    Update: Reflector gives this:

    public class List
    {
        // Methods
        public List();
        private static void Main();
        public static IEnumerable Power(int number, int exponent);
    
        // Nested Types
        [CompilerGenerated]
        private sealed class <Power>d__0 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable
        {
            // Fields
            private int <>1__state;
            private object <>2__current;
            public int <>3__exponent;
            public int <>3__number;
            private int <>l__initialThreadId;
            public int <counter>5__1;
            public int <result>5__2;
            public int exponent;
            public int number;
    
            // Methods
            [DebuggerHidden]
            public <Power>d__0(int <>1__state);
            private bool MoveNext();
            [DebuggerHidden]
            IEnumerator<object> IEnumerable<object>.GetEnumerator();
            [DebuggerHidden]
            IEnumerator IEnumerable.GetEnumerator();
            [DebuggerHidden]
            void IEnumerator.Reset();
            void IDisposable.Dispose();
    
            // Properties
            object IEnumerator<object>.Current { [DebuggerHidden] get; }
            object IEnumerator.Current { [DebuggerHidden] get; }
        }
    }
    
    IEnumerator<object> IEnumerable<object>.GetEnumerator()
    {
        List.<Power>d__0 d__;
        if ((Thread.CurrentThread.ManagedThreadId == this.<>l__initialThreadId) && (this.<>1__state == -2))
        {
            this.<>1__state = 0;
            d__ = this;
        }
        else
        {
            d__ = new List.<Power>d__0(0);
        }
        d__.number = this.<>3__number;
        d__.exponent = this.<>3__exponent;
        return d__;
    }
    
    
    
    private bool MoveNext()
    {
        switch (this.<>1__state)
        {
            case 0:
                this.<>1__state = -1;
                this.<counter>5__1 = 0;
                this.<result>5__2 = 1;
                while (this.<counter>5__1++ < this.exponent)
                {
                    this.<result>5__2 *= this.number;
                    this.<>2__current = this.<result>5__2;
                    this.<>1__state = 1;
                    return true;
                Label_0065:
                    this.<>1__state = -1;
                }
                break;
    
            case 1:
                goto Label_0065;
        }
        return false;
    }
    

    Back in the good old days, before we had the yield operator, we used to write classes which implemented IEnumerator.

    class PowerEnumerator : IEnumerator<int>
    {
      private int _number;
      private int _exponent;
      private int _current = 1;
    
      public PowerEnumerator(int number, int exponent)
      {
        _number = number;
        _exponent = exponent;
      }
    
      public bool MoveNext()
      {
        _current *= number;
        return _exponent-- > 0;
      }
    
      public int Current
      {
        get
        {
          if (_exponent < 0) throw new InvalidOperationException();
          return _current;
        }
      }
    }
    

    Or something like that. It wasn't fun, let me tell you.


    First off, yield is not an operator. yield return and yield break are statements.

    There are plenty of articles available on how the compiler implements iterator blocks. Start by reading the C# specification section on iterator blocks; it gives some suggestions for how an implementer of C# might want to go about it.

    Next read Raymond Chen's series "The implementation of iterators in C# and its consequences"

    http://www.bing.com/search?q=raymond+chen+the+implementation+of+iterators

    Next, read Jon Skeet's book chapter on the subject:

    http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx

    If after all that you are still interested then read my series on the design factors that went into this feature:

    http://blogs.msdn.com/b/ericlippert/archive/tags/iterators/


  • Let .NET Reflector decompile it. It's a generic solution (a state machine actually), but quite complex, > 20 lines of codes if I remember correctly.
  • Lazy. That's the point why yield can be quite efficient.
  • 链接地址: http://www.djcxy.com/p/54312.html

    上一篇: 收益声明对程序流的影响

    下一篇: C#:如何翻译收益关键字