通过调用Yield Return返回结果

时间:2013-08-28 10:53:54

标签: c# performance lazy-evaluation yield-return

我经常发现使用yield return语句返回IEnumerable的场景,然后有其他方法使用不同的参数调用此函数并直接返回结果。

与简单地返回集合相比,迭代结果yield return是否有任何性能优势?即如果我在生成的IEnumberable上使用return而不是再次循环遍历这些结果并使用yield return,编译器知道只生成需要的结果,或者它是否等待整个集合在返回所有结果之前返回?

public class Demo
{
    private IEnumerable<int> GetNumbers(int x)
    {
        //imagine this operation were more expensive than this demo version
        //e.g. calling a web service on each iteration.
        for (int i = 0; i < x; i++)
        {
            yield return i;
        }
    }
    //does this wait for the full list before returning
    public IEnumerable<int> GetNumbersWrapped()
    {
        return GetNumbers(10);
    }
    //whilst this allows the benefits of Yield Return to persist?
    public IEnumerable<int> GetNumbersWrappedYield()
    {
        foreach (int i in GetNumbers(10))
            yield return i;
    }
}

4 个答案:

答案 0 :(得分:3)

GetNumbersWrapped只是传递原始的可枚举 - 它甚至没有在那时调用迭代器。最终结果仍然是完全延迟/延迟/假脱机/其他任何内容。

GetNumbersWrappedYield添加了一个额外的抽象层 - 所以现在,每次调用MoveNext都必须做更多更多的工作;不足以引起疼痛。它也将完全延迟/延迟/假脱机/其他任何东西 - 但增加了一些小的开销。通常这些小的开销是通过添加某些值(如过滤或其他处理)来证明的。但在这个例子中并不是真的。

注意:GetNumbersWrappedYield 做的一件事是防止来电者滥用。例如,如果GetNumbers是:

private IEnumerable<int> GetNumbers(int x)
{
    return myPrivateSecretList;
}

然后GetNumbersWrapped的来电者可以滥用

IList list = (IList)GetNumbers(4);
list.Clear();
list.Add(123); // mwahahaahahahah

然而,GetNumbersWrappedYield 的来电者不能这样做...至少,不是那么容易。当然,他们仍然可以使用反射将迭代器分开,获取包装的内部引用,并投射

然而,这通常不是真正的问题。

答案 1 :(得分:1)

返回IEnumberable<T>的函数确实返回了一个对象,该对象不是完整列表,但知道在调用Enumerator的{​​{1}}方法时如何迭代它。您的MoveNext方法也是如此。它不等待完整的收集。它返回一个内部有GetNumbersWrapped的对象。每当Enumerator循环(或其他循环操作)调用此枚举器的foreach方法时,它就会开始读取值。所以MoveNextGetNumbersWrapped是相同的,只是GetNumbersWrappedYield有一个冗余循环层。

答案 2 :(得分:1)

如果我正确理解了你的问题,函数GetNumbersWrapped在返回之前不会等待完整列表,因为它的返回类型是IEnumerable,并且GetNumbers内部循环的执行被推迟。 / p>

答案 3 :(得分:1)

您可能知道,IEnumerable<T>会被懒惰地评估。这意味着所有三种方法都将有效地完全相同,但GetNumbersWrappedYield将只包含在冗余枚举器中。这样做不会有任何性能上的好处,事实上,额外的枚举器会带来轻微的开销。我会直接坚持GetNumbers