在C#中返回IEnumerable实例和yield return语句之间的确切区别是什么?

时间:2010-09-17 07:19:03

标签: c# ienumerable

目前我正在使用一些通过迭代器应用延迟执行的库。在某些情况下,我需要简单地“转发”收到的迭代器。即我必须从被调用的方法中获取IEnumerable<T>实例并立即返回。

现在我的问题:简单地返回收到的IEnumerable<T>或通过循环重新产生它之间是否存在相关区别?

IEnumerable<int> GetByReturn()
{
    return GetIterator(); // GetIterator() returns IEnumerable<int>
}
// or:
IEnumerable<int> GetByReYielding()
{
    for(var item in GetIterator()) // GetIterator() returns IEnumerable<int>
    {
        yield return item;
    }
}

5 个答案:

答案 0 :(得分:4)

在阅读Jon Skeet关于C#迭代器的文章时可能值得一试。这是非常有用的信息。

http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx

答案 1 :(得分:3)

他们是不同的。例如,如果GetIterator()声明为:

IEnumerable<int> GetIterator() {
    List<int> result = new List<int>();
    for(int i = 0; i < 1000; i++) {
        result.Add(i);
    }
    return result;
}

如果你没有重新屈服,GetIterator()和循环立即执行。因此,答案取决于您如何实施GetIterator()。如果确定GetIterator()将会屈服,那么就没有必要重新产生它。

答案 2 :(得分:1)

除了代码膨胀之外,我没有看到任何相关的区别。

答案 3 :(得分:1)

两者之间没有任何相关的区别(除了可能表现),因为你没有对GetIterator()的枚举器做任何事情。如果你要对枚举器做一些事情,比如过滤它就会重新产生。

答案 4 :(得分:0)

存在相关差异。

GetByReYielding()的执行将以延迟的方式执行(因为它是一个迭代器块)。如果您在GetByReturn()或GetByReYielding()中使用了一个参数并在运行时检查该参数是否为null(或进行了任何其他验证),则在调用GetByReturn()时立即执行此检查,但在调用GetByReYielding()时不立即执行此检查!当迭代结果时,GetByReYielding()中的验证将以延迟方式执行。 - 这通常是“太晚了”。见这里:

// Checks parameters early. - Fine. The passed argument will be checked directly when
// GetByReturn() is called.
IEnumerable<int> GetByReturn(IEnumerable<int> sequence)
{
    if(null == sequence)
    {
        throw new ArgumentNullException("sequence");
    }

    return GetIterator();
}
// Checks parameters in a deferred manner. - Possibly not desired, it's "too" late. I.e.                 // when the    
// result is iterated somewhere in a completely different location in your code the 
// argument passed once will be checked.
IEnumerable<int> GetByReYielding(IEnumerable<int> sequence)
{
    if(null == sequence)
    {
        throw new ArgumentNullException("sequence");
    }

    for(var item in GetIterator()) 
    {
        yield return item;
    }
}

先生。 Skeet在http://msmvps.com/blogs/jon_skeet/archive/2010/09/03/reimplementing-linq-to-objects-part-2-quot-where-quot.aspx中解释了这个概念。 .Net中提供的标准查询运算符使用非延迟包装函数(例如Where())来检查参数,然后调用核心迭代器函数(正如我在GetByReturn()的实现中所示)。

我希望这会有所帮助。