简体   繁体   中英

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.

Now my question: Is there a relevant difference between simply returning the recieved IEnumerable<T> or re-yielding it via a loop?

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. It's quite informative.

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

They ARE different. For example, if the GetIterator() declared as:

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. Therefore, the answer depends on how you implement GetIterator() . If it is certain that GetIterator() will be yielding, so there is no point re-yielding it.

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

There isn't any relevant difference (aside from maybe performance) between the two since you're not doing anything with the enumerator from 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). 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! The validation in GetByReYielding() would be performed the deferred way, when the result is iterated. - 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 . 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()).

I hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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