Coder Perfect

Use of the term ‘yield return’ correctly

Problem

The yield keyword in C# is one of those that has always perplexed me, and I’ve never been sure if I’m using it right.

Which of the following two lines of code is favored, and why?

Using yield retur in version 1

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        foreach (Product product in products)
        {
            yield return product;
        }
    }
}

Return the list in version 2

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        return products.ToList<Product>();
    }
}

Asked by senfo

Solution #1

When I calculate the next item on the list, I usually use yield-return (or even the next group of items).

Before returning, you must have the entire list on your Version 2. When you use yield-return, you only require the next item before returning.

This, among other things, helps spread the cost of difficult calculations over a longer period of time. You never calculate the last items in the list if the list is connected to a GUI and the user never travels to the last page.

When the IEnumerable represents an endless set, yield-return is also preferable. Consider a list of Prime Numbers, or a list of infinite random numbers. Because you can never return the entire IEnumerable at once, yield-return is used to return the list in chunks.

Because you have the complete list of products in your case, I’d go with Version 2.

Answered by abelenky

Solution #2

Filling a temporary list is the same as downloading the entire video, whereas utilizing yield is the same as streaming it.

Answered by anar khalilov

Solution #3

Let’s pretend that the function ConsumeLoop() processes the items returned/yielded by ProduceList() as an example of when you should utilize yield:

void ConsumeLoop() {
    foreach (Consumable item in ProduceList())        // might have to wait here
        item.Consume();
}

IEnumerable<Consumable> ProduceList() {
    while (KeepProducing())
        yield return ProduceExpensiveConsumable();    // expensive
}

The call to ProduceList() without yield may take a long time because you must finish the list before returning:

//pseudo-assembly
Produce consumable[0]                   // expensive operation, e.g. disk I/O
Produce consumable[1]                   // waiting...
Produce consumable[2]                   // waiting...
Produce consumable[3]                   // completed the consumable list
Consume consumable[0]                   // start consuming
Consume consumable[1]
Consume consumable[2]
Consume consumable[3]

It’s rearranged, sort of interleaved, using yield:

//pseudo-assembly
Produce consumable[0]
Consume consumable[0]                   // immediately yield & Consume
Produce consumable[1]                   // ConsumeLoop iterates, requesting next item
Consume consumable[1]                   // consume next
Produce consumable[2]
Consume consumable[2]                   // consume next
Produce consumable[3]
Consume consumable[3]                   // consume next

Finally, you should use Version 2 because you already have the completed list, as many others have indicated.

Answered by Kache

Solution #4

I realize this is an old question, but I’d want to provide an example of how the yield keyword might be utilized in a unique way. This method has been quite beneficial to me. Hopefully, this will be useful to anyone else who comes over this problem.

Note: Don’t consider the yield keyword to be just another means to create a collection. The notion that execution in your method or property is delayed until the calling code iterates over the next value is a key component of yield’s capability. Here’s an illustration of what I mean:

I can express an asynchronous call to a web service like this using the yield keyword (along with Rob Eisenburg’s Caliburn.Micro coroutines implementation):

public IEnumerable<IResult> HandleButtonClick() {
    yield return Show.Busy();

    var loginCall = new LoginResult(wsClient, Username, Password);
    yield return loginCall;
    this.IsLoggedIn = loginCall.Success;

    yield return Show.NotBusy();
}

This will turn on my BusyIndicator, call my web service’s Login method, set my IsLoggedIn flag to the return value, and then turn off the BusyIndicator.

This is how it works: There is an Execute method on IResult, as well as a Completed event. Caliburn. Micro takes the IEnumerator from the HandleButtonClick() method and puts it in a Coroutine. The method BeginExecute is used to start the execution process. BeginExecute is a method that iterates through IResults. Inside HandleButtonClick(), execution is paused when the first IResult is returned, and BeginExecute() attaches an event handler to the Completed event and runs Execute (). IResult.Execute() can do either a synchronous or asynchronous task, and after it’s finished, it triggers the Completed event.

The following is an example of a LoginResult:

public LoginResult : IResult {
    // Constructor to set private members...

    public void Execute(ActionExecutionContext context) {
        wsClient.LoginCompleted += (sender, e) => {
            this.Success = e.Result;
            Completed(this, new ResultCompletionEventArgs());
        };
        wsClient.Login(username, password);
    }

    public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
    public bool Success { get; private set; }
}

Setting up something like this and walking through the execution to see what’s going on would be beneficial.

I hope this is of assistance to someone! I’ve had a lot of fun learning about the various applications of yield.

Answered by Adam W. McKinley

Solution #5

Yield return can be quite useful in algorithms that require iterating through millions of items. Consider the following scenario, in which you must compute possible ride-sharing trips. First, we create a list of probable journeys:

    static IEnumerable<Trip> CreatePossibleTrips()
    {
        for (int i = 0; i < 1000000; i++)
        {
            yield return new Trip
            {
                Id = i.ToString(),
                Driver = new Driver { Id = i.ToString() }
            };
        }
    }

Then repeat the process for each trip:

    static void Main(string[] args)
    {
        foreach (var trip in CreatePossibleTrips())
        {
            // possible trip is actually calculated only at this point, because of yield
            if (IsTripGood(trip))
            {
                // match good trip
            }
        }
    }

If you use List instead of yield, you’ll have to allocate 1 million items to memory (190 MB), and this simple example will take 1400 milliseconds to run. However, if you use yield, you won’t have to store all of these temporary objects in memory, and you’ll get a far faster algorithm: this example will run in less than 400 milliseconds with no memory usage.

Answered by Andzej Maciusovic

Post is based on https://stackoverflow.com/questions/410026/proper-use-of-yield-return