收益声明对程序流的影响

我试图理解C#中yield关键字的用法,因为我使用的队列建模软件包广泛使用它。

为了演示yield的使用,我正在玩弄以下代码:

using System;
using System.Collections.Generic;
public class YieldTest
{
    static void Main()
    {
        foreach (int value in ComputePower(2, 5))
        {
            Console.Write(value);
            Console.Write(" ");
        }
        Console.WriteLine();
    }
    /**
     * Returns an IEnumerable iterator of ints
     * suitable for use in a foreach statement
     */
    public static IEnumerable<int> ComputePower(int number, int exponent)
    {
        Console.Write ("Arguments to ComputePower are number: " + number + " exponent: " + exponent + "n");
        int exponentNum = 0;
        int numberResult = 1;
        while (exponentNum < exponent)
        {
            numberResult *= number;
            exponentNum++;
            // yield: 
            // a) returns back to the calling function (foreach),
            // b) updates iterator value (2,4,8,16,32 etc.)
            yield return numberResult;
        }
    }
}

代码的作用是非常明显的,它使用ComputePower提供了一个强大的功能,它返回一个IEnumerable 。 在调试代码时,我看到yield语句将控制权返回给foreach循环,并且value变量用最新的权力结果更新。 2,4,8,16,32。

没有充分了解使用的yield ,我期望ComputePower被称为多次通过迭代值ComputePower和我会看到"Arguments to ComputePower are "等台写发生5次。 实际发生的事情是,似乎ComputePower方法只被调用一次。 我每次运行只看到一次"Arguments to ComputePower.."字符串。

有人可以解释为什么会出现这种情况吗? 它是否与yield关键字有关?


foreach将迭代从ComputePower返回的IEnumerable。 “收益回报”会自动创建IEnumerable的实现,因此您不必手动滚动它。 如果你在你的“while”--loop中放置一个断点,你会看到每次迭代都会调用它

从msdn:

您可以使用foreach语句或LINQ查询来使用迭代器方法。 foreach循环的每次迭代调用迭代器方法。 在迭代器方法中达到yield return语句时,将返回expression,并保留代码中的当前位置。 下一次调用迭代器函数时,将从该位置重新启动执行。


在高层次上,您可以将yield视为“返回一个值并冻结该方法的当前状态”。 当发电机下一次被调用时,该方法将解冻并从收益率后的线开始恢复。 因此,只有在方法开始时的任何行才会被调用一次,而不是在存在yield的循环中,它不会重新启动整个方法。

在低级别, yield通过编译器将您的方法转换为状态机来实现,其中在方法的开始处添加了跳转表,以及我们执行的跳转(在调用该函数时我们开始执行的代码行方法)由发生器所处的'状态'确定。类似的编码技术用于等待/异步状态机,并且允许程序员在易于理解的模型下隐藏很多复杂性。


yield return会导致编译器构建一个状态机,该状态机使用方法的主体实现IEnumerable<T> 。 它从你的方法中返回一个对象,而不会在你编写它的时候真正调用你的方法的主体 - 编译器用更复杂的东西代替它。

在状态机生成的IEnumerator<T>上调用MoveNext()时(例如在foreach循环期间),状态机将执行您的方法代码,直到它达到第一个yield return语句。 然后它将Current的值设置为您返回的任何值,然后将控制返回给调用者。

在实践中,看起来您的方法体每次迭代都会执行一次,并且每次达到yield return语句时循环都会“中断”。

如果您在方法的while循环中放置断点,则会看到堆栈包含对您的方法正文已成为编译器生成类型的编译器生成类型的MoveNext()

链接地址: http://www.djcxy.com/p/54313.html

上一篇: Yield statement's effect on program flow

下一篇: C#: How to translate the Yield Keyword