C#: 如何翻译Yield关键字

4
  1. 如果没有yield关键字,MSDN示例会是什么样子?如果您喜欢,可以使用任何示例。我只想了解在幕后发生了什么。
  2. yield操作符是急切地还是懒惰地评估的?
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);
        }
    }
}

如果 yield 运算符在此处被急切地计算,这是我的猜测:

    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;
    }

我不知道如果yield操作符进行惰性求值会是什么样子。
更新:反射器给出了以下结果:
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;
}

如果这是一份作业,你应该标记它。 - ChristopheD
当然,这是个人发展作业。 - user295190
这是 https://dev59.com/eXRB5IYBdhLWcg3wCjcV 的完全副本。 - Eric Lippert
4个回答

11

首先,yield不是一个运算符。yield return和yield break是语句。

有很多关于编译器如何实现迭代器块的文章可供参考。首先阅读C#规范中关于迭代器块的部分;它给出了一些实现C#的建议。

接下来阅读雷蒙德·陈(Raymond Chen)的系列文章《C#中迭代器的实现及其后果》

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

接着,阅读乔恩·斯基特(Jon Skeet)关于此主题的书籍章节:

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

如果在所有这些内容之后您仍然对此感兴趣,请阅读我关于迭代器块设计因素的系列文章:

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


谢谢Eric,那里有很多要阅读的内容。这是一个有趣的陈述(不是操作符笑)。 - Pete855217

7
在拥有yield操作符之前的好日子里,我们曾经编写实现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;
    }
  }
}

或者类似的东西。相信我,那一点也不好玩。

(这段话已经是中文了,无需翻译)

2
显然,我已经实现了通用版本,尽管那也不可用。我们不得不步行5英里才能开始构建。请注意,你可以花3.50英镑出去玩一晚上,还有足够的零钱买一袋薯条和坐公交回家。 - Mark Rendle
1
阿门。如果我再也不必构建整个类来获得ArrayList的强类型版本,那对我来说是好事。 - LoveMeSomeCode
我很欣赏Eric Lippert的回答,但是这个答案正中要害。 - user295190

0
  1. 这将是一个自定义实现的 IEnumerable<T>,而不是依赖于现有的实现,如 List<T>
  2. 惰性地执行。

更多信息可在此处找到。


0
  1. 使用.NET Reflector进行反编译。这是一个通用解决方案(实际上是状态机),但相当复杂,如果我没记错的话,代码行数超过20行。
  2. 懒惰。这就是为什么yield可以非常高效的原因。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接