简体   繁体   English

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

[英]What is the exact difference between returning an IEnumerable instance and the yield return statement in C#

Currently I'm working with some libraries applying deferred execution via iterators. 目前我正在使用一些通过迭代器应用延迟执行的库。 In some situations I have the need to "forward" the recieved iterator simply. 在某些情况下,我需要简单地“转发”收到的迭代器。 Ie I have to get the IEnumerable<T> instance from the called method and return it immediately. 即我必须从被调用的方法获取IEnumerable<T>实例并立即返回它。

Now my question: Is there a relevant difference between simply returning the recieved IEnumerable<T> or re-yielding it via a loop? 现在我的问题:简单地返回收到的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;
    }
}

It may be worth your while reading Jon Skeet's article on C# Iterators. 阅读Jon Skeet关于C#迭代器的文章可能值得你去参考。 It's quite informative. 这是非常有益的。

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

They ARE different. 它们是不同的。 For example, if the GetIterator() declared as: 例如,如果GetIterator()声明为:

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

If you do not do re-yielding, the GetIterator() and the loop got executed immediately. 如果不进行重新生成, GetIterator()立即执行GetIterator()和循环。 Therefore, the answer depends on how you implement GetIterator() . 因此,答案取决于您如何实现GetIterator() If it is certain that GetIterator() will be yielding, so there is no point re-yielding it. 如果确定GetIterator()将会屈服,那么重新产生它就没有意义了。

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

There isn't any relevant difference (aside from maybe performance) between the two since you're not doing anything with the enumerator from GetIterator() . 两者之间没有任何相关的差异(除了可能的性能),因为你没有使用GetIterator()的枚举器做任何事情。 It would only make sense to re-yield if you were going to do something with the enumerator, like filter it. 如果您要对枚举器执行某些操作(例如过滤器),那么重新收益才有意义。

There is a relevant difference. 有一个相关的区别。

The execution of GetByReYielding() will be executed in a deferred manner (as it is an iterator block). GetByReYielding()的执行将以延迟方式执行(因为它是迭代器块)。 If you used a parameter in GetByReturn() or GetByReYielding() and checked that parameter at runtime for nullity (or did any other validation), this check would be done immediately when GetByReturn() is called but not immediately when GetByReYielding() is called! 如果您在GetByReturn()或GetByReYielding()中使用了一个参数并在运行时检查该参数是否为null(或进行了任何其他验证),则在调用GetByReturn()时立即执行此检查,但在调用GetByReYielding()时不立即执行此检查! The validation in GetByReYielding() would be performed the deferred way, when the result is iterated. 当迭代结果时,GetByReYielding()中的验证将以延迟方式执行。 - This is often, well, "too late". - 这通常是“太晚了”。 See here: 看这里:

// 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;
    }
}

Mr. Skeet explained this concept in http://msmvps.com/blogs/jon_skeet/archive/2010/09/03/reimplementing-linq-to-objects-part-2-quot-where-quot.aspx . Skeet先生在http://msmvps.com/blogs/jon_skeet/archive/2010/09/03/reimplementing-linq-to-objects-part-2-quot-where-quot.aspx中解释了这个概念。 The standard query operators provided in .Net use non-deferred wrapper functions (eg Where()) that check parameters and then call the core iterator function (as I showed in my implementation of GetByReturn()). .Net中提供的标准查询运算符使用非延迟包装函数(例如Where())来检查参数,然后调用核心迭代器函数(正如我在GetByReturn()的实现中所示)。

I hope this helps. 我希望这有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM