开发者

When is 'Yield' really needed? [duplicate]

开发者 https://www.devze.com 2023-01-03 02:05 出处:网络
This question already has ans开发者_开发问答wers here: Closed 12 years ago. Possible Duplicate: C# - Proper Use of yield return
This question already has ans开发者_开发问答wers here: Closed 12 years ago.

Possible Duplicate:

C# - Proper Use of yield return

What can be a real use case for C# yield?

Thanks.


When you want deferred execution.

This makes sense in most cases where the alternative is to construct a temporary collection.

Consider this scenario: I have a list of integers and I want to list their squares.

I could do this:

public static IEnumerable<int> Squares(this IEnumerable<int> numbers) {
    List<int> squares = new List<int>();
    foreach (int number in numbers)
        squares.Add(number * number);

    return squares;
}

Then I could sum the squares, take their average, find the greatest, etc.

But I really didn't need to populate a whole new List<int> for that purpose. I could've used yield to enumerate over the initial list and return the squares one-by-one:

public static IEnumerable<int> Squares(this IEnumerable<int> numbers) {
    foreach (int number in numbers)
        yield return number * number;
}

The fact that this actually makes a difference might not be apparent until you start dealing with very large collections, where populating temporary collections proves to be quite wasteful.

For example suppose I wanted to find the first square above a certain threshold. I could do this:

IEnumerable<int> numbers = GetLotsOfNumbers();
var squares = numbers.Squares();
int firstBigSquare = squares
    .Where(x => x >= 1000)
    .FirstOrDefault();

But if my Squares method populated an entire List<int> before returning, the above code would be doing potentially far more work than necessary.


From the MSDN page on yield:

Used in an iterator block to provide a value to the enumerator object or to signal the end of iteration.

You use it when creating a custom iterator. Using the example from the page:

// yield-example.cs
using System;
using System.Collections;
public class List
{
    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);
        }
    }
}

The yield means that the while loop inside Power effectively "pauses" after each iteration to allow the calling routine to perform some action. In this case printing out the result.


When you're too lazy to write your own IEnumerator ;)


See this article.

Yield acts as a return-placeholder - it's a non-local goto return point which preserves the method's environment and allows the code to "jump" back in. In a way similar (kind of inverted) to passing a delegate into a method which allows you to inject specific logic within another method, closures allow you to do different types of work "around" a more general method, allowing you to keep code small and modular and re-usable.

This could make for much more efficient code. Instead of instantiating a very large collection, it might be possible to allow individual objects to be acted upon in sequence(and they are discarded after each operation). I imagine you could construct cases where a straightforward iterator would be extremely difficult to build.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号