BrandGhost

Intro To Yield In C# - Beginner Examples In Action

The yield keyword is a spicy one in C#. That's because when we use it within a method that returns IEnumerable, we get this fancy thing called an "iterator". Yeah. It's fancy as heck. But it can also be really confusing to understand, which is exactly why I made this video to help you out. Let's step through some code so you can see the behavior of the yield keyword and how it works inside of iterators!
View Transcript
hi my name is Nick centino and I'm a principal software engineering manager at Microsoft in this video I wanted to talk to you about the yield keyword in C and how that fits in with iterators now I've made videos about this before but I think this is one of those topics that having more examples and more things to walk through can really help solidify the idea if this is something you struggle with I'm hopeful that having one more video to go look through will help you if that sounds interesting just a remind you to subscribe to the channel and check out that pin comment for my courses on dome train now let's jump over to visual studio and look at the yield keyword on my screen right now you can see that there are a couple of lines that have the yield keyword so on line three we can see yield return one 23 and line a we also see yield return one 23 same code but if I go uncomment this block of code up at the top you'll see that Visual Studio is not happy with this method if I hover over it it's suggesting that the body of this method is not an iterator it cannot be and that's because the int return type is not an iterator interface so what the heck does that even mean if we think about what the yield keyword is responsible for doing what it's able to do for us when we yield return is that we're able to return back some item from a method but the next time that this method is itrated on it will come back to where this code was executing so for example if I had yield 1 2 3 then yield 4 56 when yield 1 2 3 completes the next iteration so the next time that this method is entered it will start right back here and now we will go yield return 456 the problem is that int as a return type is not an iterator like int is one single item but we have a type we have an interface that allows us to work with this and that's going to be I inumerable so this block of code up at the top simply won't work because int is a single item it is not an i inumerable this block from Line 6 to 9 is what is called an iterator and to have an iterator you need two ingredients you need an iable return type and you need yield return what this allows us to do is when we call this method like I said it will enter and then it will end up returning one 2 3 and the next iteration it will come back to yield one 123 and then try to execute the code after and because there's no code after it will just finish but if we did this again put four five6 if we have this now it will in fact come back and the next item will be this one so let's go ahead put a couple of break points on here and run this code so something peculiar has happened so if we look at what's happening in the application we can see that on line 12 we are calling yield ins and then we are doing right line however my break points never got hit I simply pressed play and then we got to this point it says enter to continue which is really at the end of the program before reading from the console but how did we not hit our break points we literally called the method online 12 so if I press enter now it will end but how come we didn't go into here that's because this is a property of iterators and this is in my opinion one of the most confusing things when people are using iterators because they're not expecting this Behavior if they're not familiar with iterators this line line 12 is essentially only doing an assignment of a function pointer and that might seem incredibly confusing because you're saying Nick I literally see right here that we are calling this method and that's how it looks I get it for sure but what's actually happening here is that this is a function and we're just assigning it to this variable this is not actually invoking this function and the reason that's not happening is because this is an iterator we have not yet done any iteration on it so I realize that's probably confusing I'll say it one more time this line here is only assigning a function pointer into this variable it is not executing the iteration on this method in order for us to do that we need to actually perform iteration to cause these break points to get hit let's instead look at this code that I've just added here which has a four each Loop calling yield ins now of course this is slightly different I mean this code rate here is very similar but what I'm doing now is I'm walking through every element that comes back from this iterator this is iterating over the iterator so if I go run this now right the first block of code still did not get hit because like I said line 12 is just assigning a function pointer if I press enter at this point we will see that we get 1 2 3 so this break point is actually hit now if I press f11 you'll see that it's coming back into this 4 each Loop if I hover over here it's going to say got number and then 1 2 3 is the value that comes out of that if I press f11 again let's see where we end up so we'll walk through through and now we're back up at the top of the for reach Loop and we're going to ask the iterator do you have anything else and if we do that see how now we've jumped to line nine so it started execution back after line 8 we've already run line 8 from our iterator that's what gave us 1 2 3 the first time and now it's come back to where it needed to continue executing which is at line nine so returning 456 if I press f11 again we'll come back to this 4 each Loop and now we will write out 456 from there if I just finish executing it if we look at the console we can see that we got both of these numbers coming back out before we continue on this is just a quick reminder that I do have courses available on dome train if you're just getting started in your programming journey and you want to learn C you can head over to dome train I have a getting started in C course it's approximately 5 hours of content taking you from absolutely no experience to being able to program in C and after that I have my deep dive course which will take you to the next level with another 6 hours of content so that you can start building basic applications head over to dome prane and check it out let's head back to the video now I did mention that this is a function pointer so what I want to do is show you that if I copy this from line 12 and I put it here right if I run this now it will have the exact same behavior so again the first time this part here from line 12 to 14 that's not actually going to hit any break points and we'll see that just as I pressed F5 and this spun up we can see enter to continue but as soon as I press enter and we go into this part on line 16 enter as soon as that happens you can see Visual Studio has this in keyword kind of grayed out but that's suggesting that's on the call stack here and now we can see we've hit line eight where it wants to yield return 1 2 3 so pressing f11 again same pattern that we saw before now the next thing that I want to bring up because this is yet again something else that people seem to get stuck on or they end up making mistakes because they don't realize it but an iterator when we do this type of assignment here this is not a collection I know that it looks like it because we literally have 1 2 3 and four 5 Six coming back however it's not a materialized collection it's not a list and it's not an array that means that if you were to call yield ins again or in fact if we were to call yield ins result one more time it is going to execute this method one more time and just to prove it to you what I'm going to do is copy and paste this let's go ahead and comment out this code up here so we don't have to go wait but I'll put a breakpoint here and I'll put a breakpoint here so when we start these four each Loops we can see it stepping into the code what we should observe is that this method literally gets called twice in its entirety and that's because yield int results the second time we go to do this it's not working with an array or a list or a materialized collection if it literally has to go run the entire method once more and after I show you this I'll explain why that could be impactful starting things off I hit the breako on line 16 pressing f11 a couple of times we'll start to get into this yield in iterator so coming back out right we're going to print out 1 2 3 then 4 5 6 this is exactly what we saw before now when I get down to the next for each Loop here remember I did say that there's no array and there's no list this stuff has not been materialized anywhere so when we go to do this once more it will go back into the method and it's going to repeat the entire thing so you might be saying well Nick that doesn't really matter because this is so simple 1 2 3 and four 5 six we just have two numbers and you're totally right this example is very basic that's the whole point of me trying to make these really simple videos however what I want to do is explain why this could be tremendously impactful when I show you these types of tutorial obviously the code examples are very simple but I want you to try and think about what this could look like in production code because the reality is I'm hoping that you're trying to build things and that you're coming across different challenges and looking up tutorials just like this one to see how it will fit into your code this yield return example here is very simple but let's imagine for a moment that this method was instead trying to e yeed back results from a database we've executed a query and we're saying hey for each it item that comes back let's yield return it what that ends up looking like is something like this where we say at the beginning we need to open a connection and then from there we need to execute the query that co-pilot's trying to help me out here so that's great and then we would do something like uh read the results and yield them back okay so this is sort of the pattern that you would have if you were working with a database right so again open the connection let's imagine that this takes I don't know um copilot saying takes a long time yes for a computer relatively it's going to be a long time but let's say it takes 500 milliseconds I'm just making up numbers right execute the query takes um let's say it's actually a pretty slow query it's going to take one full second so 1 th000 milliseconds and then actually reading the results back is pretty quick so let's say it's only 10 milliseconds so it's streaming the results and it's pretty quick these are just totally contrived numbers but I just want you to think about what this this means if we had to go run this code again and we had to do code like this where we needed the results twice what's going to happen is that for this block of code from line 20 to 23 it will take 500 milliseconds plus 1,000 millisecs plus 10 so 1510 milliseconds in total to run this block of code now if we were to go call this code again it's going to go repeat the exact same thing and that doesn't mean that it's just used the same results that we just got it literally means that we will go make a connection go read back the results and yield them right so you are paying that performance penalty one more time so in different applications this could have a tremendous impact one of the ways that you can work around this is putting these things into a collection so just as an example if we imagine that this stuff was actually running maybe I'll actually go ahead and I will put some thread sleeps in here so we could do um thread. sleep and I said 500 milliseconds here and then we'll do a th000 we'll see what this actually looks like right so we'll do 10 here this is super quick but doing this type of thing uh and it's going to be yielding them back so it's not 10 in this example it's not 10 per uh result it's just going to be 10 uh to get both numbers back this part that I have highlighted is functionally what we had before but I'm trying to show you that we're simulating some real world penalties going on here let's go ahead and run this example now I'm going to go ahead and take these break points off and let's see what happens starting things off we see that we get the numbers and then it had a delay before the other two numbers it's pretty quick because I didn't pick really long times but if I run it one more time and we pay close attention there's a delay then the numbers and then the other numbers so there's a delay twice between the two sets of numbers one way that you can work around this and you need to think about this from an engineering perspective is what do you want to trade do you want to trade the performance in terms of the runtime or do you want to trade memory so in this case the way that this code is set up it is saying we're conserving memory but we're trading that for performance so we're sacrificing our performance we have to go run the query twice that's going to have some network resources and things like that again this is a very simple example but we're doing the work twice but we didn't have to hold it in memory but we could hold it in memory so let's see what that looks like so I'm going to say um let's put this into a list of ins and I will say materialized so what I'm going to do is make a materialized list of these items and instead of just doing two list on the results I'm going to add them through the first time so uh materialize we can add the numbers and then afterwards I'm going to use this down here in this example what we should see happening is that this code here will end up going into this iterator the first time we're going to run this iterator the exact same way we've seen multiple times now and every item that comes back I will add it to a collection it's called materializing when you're taking the results of something and storing it now when we have this materialized collection I can go walk through that materialized collection once more so in this part from line 29 to 32 I'm no longer running that iterator a whole second time instead what I'm doing is just saying hey look we got these things in memory already CU we've already done this let's just go write them out so what we should see is that the second set of numbers pops up almost instantaneously let's check it out there's the delay in the beginning and then all of the numbers just blast up at once and again that's because these two came from the materialized collection in memory we did not have to go back into the iterator another hole time I'm going to run this once more and just to prove to you conclusively that this is truly only running one time I'm going to write this part on line 17 out to the console and that way we can see that if this only gets printed out once and we've only run this entire method one time and you can see that after we get both of these numbers back we see yield ins end and then these two numbers get printed out but we don't see this get written to the console again so truly it is coming from the results now people can easily make a mistake here if they're not doing this and instead they're just doing an assignment like I showed you earlier if someone said like this and I'm not blaming a VAR for this by the way I'm just using it as an example if someone had code that looked like this right it's very easy to assume sometimes when you're working with iterators and you're not paying close attention you might think like hey look this yield in method call here is giv me a materialized collection so what I'm going to do is assign it to a variable and use it again but if we go do this once more we will go see that it prints out yield ins twice now so you do need to be very careful when you're doing this type of thing and working with iterators you need to make a conscious decision about whether you want something materialized or not if this database call was returning a million records you may want to think twice before trying to jam all of those into memory so it's not quite as black and white as I'm making it seem you do need to have some consideration put into this before I move on I just want to show you one more example where this can get a little bit confusing and that's because we have I inumerable as the return type and we have yield return inside of here but something that could happen and it's a very easy change that you can make inside the code is to return a materialized collection from within here and I want to show you why this can be dangerous when we start to compare the calling convention of this method so if I were to instead return a collection that had 1 2 3 and 4 5 six sorry that got spaced out all funky so I'm going to do this instead now you'll see that Visual Studio is saying look we can't even write this because it's not going to continue right once we return this is no longer an iterator it's maybe more obvious from the perspective of looking at this method right but there we go that's one time now this all of a sudden is no longer lazy so again if I go run this we paid the performance penalty once but we saw all of the numbers come up very fast after so pay the performance penalty before the first two and the second two come up immediately and that's truly because this did not have an iterator involved it's the same return type I enumerable but inside it's materialized the reason that this is confusing and I just wanted to call it out is because if you were looking at a method I know I called this one yield ins so you'd want to trust that it's truly yielding the Mac but if you look at the return type right so if I hover over this we see it's an i inumerable of int what we cannot tell from the outside of this method is whether or not it's truly an iterator or if it's going to return a fully materialized collection so keep in mind that a list an array and basically every collection in C is going to be meeting the interface I enumerable but yield return also allows you to meet that interface in your method and one more final thing is that you cannot mix this convention so you cannot do this type of thing where you have a yield return and a return you basically have to decide if your method is going to be an iterator or it's going to return a materialized collection so the whole point of me showing you this part is just to say please be careful when you have I inumerable return types and the assumption that you're making many times when things are I inumerable return types people will be trying to implement iterators behind the scenes but you cannot guarantee that purely from looking at the outside and the proof is really in this line here right we might make an assumption about what this is because it's coming back is an I inumerable but it truly is a materialized collection so that is a Hands-On example of using the yield keyword and how you can fit that in with iterators hopefully you can see how that fits in with doing things like database calls and the decisions you might have to make regarding the performance trade-offs if you're looking for other videos and trying to understand more about I enumerables iterators and other types of collections you can check out these videos next thanks and I'll see you next time

Frequently Asked Questions

What is the purpose of the yield keyword in C#?

The yield keyword allows me to return an item from a method and then pause its execution, so that the next time the method is called, it resumes from where it left off. This is particularly useful for creating iterators.

Why do I need to use IEnumerable as a return type when using yield?

I need to use IEnumerable as a return type because yield return is designed to work with iterators. If I use a single item type like int, it won't work because int is not an iterator; it’s just a single value.

What happens if I try to call a method with yield return multiple times?

If I call a method with yield return multiple times, it will execute the entire method each time. This means that if the method involves resource-intensive operations, like database calls, it will repeat those operations, which can impact performance.

These FAQs were generated by AI from the video transcript.
An error has occurred. This application may no longer respond until reloaded. Reload