需要帮助理解IEnumerable中的C#yield

时间:2010-08-11 12:21:46

标签: c#

我正在阅读C#2010 Accelerated。我不知道yield

  

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

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

我也读过Some help understanding “yield”

  

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

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

6 个答案:

答案 0 :(得分:7)

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

yield break

答案 1 :(得分:2)

查看IEnumerator<T>界面;这很可能是为了澄清正在发生的事情。编译器将您的代码转换为实现IEnumerable<T>IEnumerator<T>的类。对GetEnumerator()的调用只返回类本身。

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

答案 2 :(得分:1)

是的,这是正确的,下面是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 
*/

答案 3 :(得分:1)

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

答案 4 :(得分:0)

看起来你明白了。

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

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

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

yieldMyObjectCollection中定义。

答案 5 :(得分:0)

理解yield关键字的简单方法是我们在返回时使用不需要额外的类来保存迭代结果 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