【问题标题】:Need help understanding C# yield in IEnumerable需要帮助了解 IEnumerable 中的 C# yield
【发布时间】:2011-03-28 08:28:52
【问题描述】:

我正在阅读 C# 2010 Accelerated。我不明白yield 是什么

调用 GetEnumerator 时,代码 在包含产量的方法中 语句实际上并未在 那个时间点。相反, 编译器生成一个枚举器 类,并且该类包含 产生块代码

public IEnumerator<T> GetEnumerator() { 
    foreach( T item in items ) { 
        yield return item; 
    } 
} 

我也读过Some help understanding “yield”

yield 是一个懒惰的数据生产者,仅 在第一个项目之后生产另一个项目 已被检索,而返回 列表将返回所有内容 去吧。

这是否意味着每次调用GetEnumerator 都会从集合中获得1 个项目?所以第一个电话我得到第一个项目,第二个,我得到第二个等等......?

【问题讨论】:

  • 据我了解,您的解释似乎是正确的。 Yield 基本上会在该方法中暂停执行,直到您下次调用另一个对象。

标签: c#


【解决方案1】:

理解yield关键字的简单方法是使用return时我们不需要额外的类来保存迭代的结果 收益返回关键字。一般当我们遍历集合并想要返回结果时,我们使用集合对象 保持结果。我们来看例子。

public static List Multiplication(int number, int times)

    {
        List<int> resultList = new List<int>();
        int result = number;
        for(int i=1;i<=times;i++)
        {
            result=number*i;
            resultList.Add(result);
        }
        return resultList;
    }

static void Main(string[] args)

    {
        foreach(int i in Multiplication(2,10))
        {
            Console.WriteLine(i);
        }
        Console.ReadKey();
    }

在上面的例子中,我想返回 2 十次相乘的结果。所以我创建了一个方法乘法 它返回我乘以 2 十次,我将结果存储在列表中,当我的 main 方法调用 乘法方法,控制循环循环十次并将结果结果存储在列表中。这是没有 使用收益回报。假设如果我想使用 yield return 来做到这一点,它看起来像

public static IEnumerable Multiplication(int number, int times)

          {
        int result = number;
        for(int i=1;i<=times;i++)
        {
            result=number*i;
            yield return result;
        }
    }

static void Main(string[] args)

    {
        foreach(int i in Multiplication(2,10))
        {
            Console.WriteLine(i);
        }
        Console.ReadKey();
    }

现在 Multiplication 方法略有变化,返回类型为 IEnumerable 并且没有其他列表可以容纳 结果是因为要使用 Yield 返回类型必须是 IEnumerable 或 IEnumerator 并且因为 Yield 提供有状态迭代 我们不需要额外的类来保存结果。所以在上面的例子中,当从 Main 调用 Multiplication 方法时 方法,它计算第一次迭代的结果并将结果返回给 main 方法并返回到循环和 计算第二次迭代的结果并将结果返回给 main 方法。这样 Yield 将结果返回给调用 在每次迭代中逐个方法。还有其他关键字中断与 Yield 结合使用导致迭代 停止。例如,在上面的示例中,如果我只想计算一半的乘法(10/2=5),那么 该方法如下所示:

public static IEnumerable Multiplication(int number, int times)

    {
        int result = number;
        for(int i=1;i<=times;i++)
        {
            result=number*i;
            yield return result;
            if (i == times / 2)
                yield break;
        }

    }

这个方法现在会产生 2、5 倍的乘法结果。希望这能帮助您理解 Yield 的概念。更多 信息请访问http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

【讨论】:

    【解决方案2】:

    看看IEnumerator&lt;T&gt;接口;这很可能澄清正在发生的事情。编译器获取您的代码并将其转换为实现IEnumerable&lt;T&gt;IEnumerator&lt;T&gt; 的类。对 GetEnumerator() 的调用只是返回类本身。

    该实现基本上是一个状态机,对于每次调用 MoveNext(),它都会执行代码直到下一个 yield return,然后将 Current 设置为返回值。 foreach 循环使用此枚举器遍历枚举项,在循环的每次迭代之前调用 MoveNext()。编译器确实在这里做了一些非常酷的事情,使yield return 成为该语言中最强大的构造之一。从程序员的角度来看,这只是一种根据请求延迟返回项目的简单方法。

    【讨论】:

      【解决方案3】:

      如果我理解你的问题是正确的,那么你的理解是不正确的,我很害怕。 yield 语句(yield return 和 yield break)是一个非常聪明的编译器技巧。你方法中的代码实际上被编译成一个实现 IEnumerable 的类。此类的实例是该方法将返回的内容。让我们在调用 ins.GetEnumerator() 时调用实例“ins”,当序列没有更多元素时,你会得到一个 IEnumerator (例如,遇到 yield 中断) MoveNext() 返回 false 并且进一步调用会导致异常。所以生成(下一个)元素的不是对 GetEnumerator 的调用,而是对 MoveNext 的调用

      【讨论】:

        【解决方案4】:

        考虑它的最佳方式是,当您第一次从 IEnumerator 请求项目时(例如在 foreach 中),它开始通过该方法运行,当它遇到 yield return 时,它会暂停执行并返回该项目以供您在 foreach 中使用。然后你请求下一个项目,它会从它离开的地方恢复代码并重复循环,直到遇到yield break 或方法结束。

        public IEnumerator<string> enumerateSomeStrings()
        {
            yield return "one";
            yield return "two";
            var array = new[] { "three", "four" }
            foreach (var item in array)
                yield return item;
            yield return "five";
        }
        

        【讨论】:

          【解决方案5】:

          是的,这是MSDN 的示例,说明了如何使用它

          public class List
          {
              //using System.Collections;
              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);
                  }
              }
          }
          /*
          Output:
          2 4 8 16 32 64 128 256 
          */
          

          【讨论】:

            【解决方案6】:

            看来你明白了。

            yield 用于您所描述的类的GetEnumerator,以便您可以编写如下代码:

            foreach (MyObject myObject in myObjectCollection)
            {
                // Do something with myObject
            }
            

            通过从第一个调用第二个从第二个返回第一个,依此类推,您可以遍历集合中的所有元素。

            yieldMyObjectCollection 中定义。

            【讨论】:

              猜你喜欢
              • 2017-06-19
              • 2016-05-03
              • 1970-01-01
              • 2021-01-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多