What the hell does C# yield do?

in #programming7 years ago (edited)

From the number of question on stack exchange and also working along side other devs, too few people understand the yield in C#.

It is not actually too hard to understand when you pull it apart correctly, hopefully by the end of this you will understand how to put it back together afterwards :)

First lets start with a simple example without yield:

foreach (var value in new[] {"A", "B", "C"})
{
    Console.WriteLine(value);
}

This will print the following to the text console.

A
B
C

Now to replace the array with a generator function, a function that returns an IEnumerable using yield:

IEnumerable<string> GetStrings()
{
    yield return "A";
    yield return "B";
    yield return "C";
}

foreach (var value in GetStrings())
{
    Console.WriteLine(value);
}

This prints the same results, so what is going on here?

When GetStrings() is called it instantly returns an IEnumerable type, none of the code is executed.

When the foreach loop uses the IEnumerable to request the next element the code executes until the first yield line. At this point it returns "A". It yields control back to the caller and remembers where it has got to.

The next time a string is requested the code continues from where it reached and runs up to the next yield. This returns "B".

This continues until all yield commands have been executed and the function returns and signals there are no more items in the list. You can terminate early with "yield break", as you would with "break" in a loop.

If you compile the code and fire up a debugger with breakpoints on each yield line and WriteLine() you will see the debugger jump around as described.

The really switched on people will be thinking that this sound like LINQ, the way queries jump around when you debug them. yield is how LINQ works.

I have built an entity validation engine around yield. The reason for doing this is simple, it provides the ability to stop validation on the first error or get all the errors.

Here is an example rule for some form of bank account:

IEnumerable<string> AccountHasMoney(
    Account updated, Account original)
{
    if (updated.Money < 0m)
    {
        yield return "Not enough funds for that transaction"; 
    }
} 

It takes in the entity before and after edit, this allows checking back against the original state if required by the rule.

Each rule can return a list of errors for a few reasons:

  • It allows an empty list for no error.
  • Never have to deal with a null :)
  • You are not building and growing the errors array, everything is on the fly via LINQ lazy evaluation.
  • There are composition functions that allow child rules to be lifted to the parent level. These compositions can return many errors, one per child.

yield has so many use cases. It is worth exploring and adding to your toolbox of programmer tricks :)

Sort:  

Congratulations @woz.software! You have completed some achievement on Steemit and have been rewarded with new badge(s) :

Award for the number of upvotes received

Click on any badge to view your own Board of Honor on SteemitBoard.
For more information about SteemitBoard, click here

If you no longer want to receive notifications, reply to this comment with the word STOP

By upvoting this notification, you can help all Steemit users. Learn how here!

yield, for me, is one of those Python functionalities I usually avoid because I know roughly what it does but if I really want to use it I'll have to read up the documentation on it again - to make sure I don't introduce bugs.

Yep, it gets complex. The key to it is that you avoid mutation of any state and there is 0 risk. If you have mutation then the problem is you have no idea when code will run and hence when the mutation will take place.

In C# you can force evaluation early

var oldPeople = people.Where(persion => person.Age >= 65).ToArray();

The ToArray() forces all the code to execute there and then, removes the lazy state

Liking for the Yunkers :D

upvoting/commenting for yunk tag support. I'm still in the basics of coding/programming. I don't even know the fundamentals just yet to begin to understand this. Will follow for future tutorials. Thanks for the post and thanks for sharing.

Some of this can get subtle, I like to push languages as far as I can. Been at this for 30 years :)

It gets easier as you get the feel for it, just keep learning. Don't get set in your ways, there is always a cleaner easier solution. That said if code does what it is meant to it is correct :)

Coin Marketplace

STEEM 0.15
TRX 0.12
JST 0.026
BTC 55030.03
ETH 2337.21
USDT 1.00
SBD 2.32