【问题标题】:C#: How to translate the Yield KeywordC#:如何翻译 Yield 关键字
【发布时间】:2011-06-08 15:27:06
【问题描述】:
  1. 如果没有 yield 关键字,MSDN 示例会是什么样子?如果您愿意,可以使用任何示例。我只是想了解幕后发生的事情。
  2. yield 运算符是 eagerly 还是 lazily 评估?

示例:

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 运算符被延迟评估会是什么样子。

更新: Reflector 给出了这个:

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

【问题讨论】:

标签: c# yield


【解决方案1】:
  1. 这将是IEnumerable&lt;T&gt; 的自定义实现,而不是依赖于现有实现,例如List&lt;T&gt;
  2. 懒惰。

更多信息here

【讨论】:

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

    【讨论】:

      【解决方案3】:

      在过去的美好时光里,在我们有 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;
          }
        }
      }
      

      或者类似的东西。不好玩,让我告诉你。

      【讨论】:

      • 显然我已经实现了通用版本,尽管它也不可用。我们不得不在雪地里步行 5 英里才能开始建造。请注意,您可以花 3.50 英镑度过一个夜晚,但仍然有足够的零钱买一袋薯条和一辆回家的巴士。
      • 阿门。如果我再也不必为了拥有一个强类型的 ArrayList 版本而构建整个类,那对我来说很好。
      • 我很欣赏 Eric Lipperts 的回答,但这个回答一针见血。
      【解决方案4】:

      首先,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,那里有很多值得阅读的内容。这是一个有趣的陈述(不是操作员哈哈)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-11-15
      • 2023-03-05
      • 1970-01-01
      • 2011-12-25
      • 1970-01-01
      • 1970-01-01
      • 2019-07-06
      相关资源
      最近更新 更多