C# IEnumerable: Loop me harder - [5 Rider licenses to give away]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

First of all, let me say that your tutorial quality is excellent. Structure, pacing and production values are very well done! I'm not the target audience so I can't judge fairly on the content itself, but it seemed to me to strike the sweet spot in amount of detail vs. complexity for beginners/intermediate coders.

But I feel you should include a warning to your viewers that IEnumerables are one of the most common causes of code-based performance issues. Not a problem for hobby programmers, but projects subject to commercial constraints (consistent FPS, or mobile support) will hit a wall if they are not used very carefully.

Nearly any IEnumerable/IEnumerator/LINQ call will generate garbage and waste CPU cycles. Which puts these constructs in an awkward position for Unity coding: most programmers will understand the concepts needed for usage (interfaces, and generics) earlier than the concepts needed to understand the consequences (GC strategy, boxing, duck-typing and engine critical loops).

As a result, I mostly tend to simply tell people learning Unity coding "never use Enumerables/LINQ except 'foreach on List<>'". Whenever anyone reaches the point where they see why that statement is bollocks, it means they also know enough to use them without tanking their performances.

In case you haven't chanced upon them, Jackston Dunstan did a great serie of posts on the subject:

Sorry to be sounding a bit negative here - I hope your channel continues to grow, your videos fill a much-needed void between the innumerable "what is a variable" tutorials and the "here's how to use architecture-specific behaviour to shave 0.03ms on GPU-based fluid rendering" talks :) Maybe just include a caveat on this particular subject?

👍︎︎ 8 👤︎︎ u/tiktiktock 📅︎︎ Nov 28 2021 🗫︎ replies
Captions
hello and welcome today i'm going to be talking about enumerables and enumerators now this can be a complex topic and unless you know how it's working underneath your code might not be doing exactly what you think it's doing so i'm going to show you uh what they are how to use them and actually how to make your own uh enumerables so let's start by making a class and let's just call this uh hero academy and in here we're going to have a name of the academy and let's call this uh olympus olympus i've been playing a lot of hades recently private list and let's make this a string list and this will be uh olympians and this will be equal to a new list and this will have who are the best olympians zeus was about us uh poseidon was possible and we'll do afrodity or is it aphrodite i don't know cool so over here we'll say academy equals new hero academy and just as a normal object we can you know see the name there we obviously can't see these olympians as it's a private list but let's say we wanted to uh loop over this object so we wanted to say 4h bar name in academy obviously this is not going to work because it's saying hero academy cannot use 4-h because it doesn't implement high innumerable so let's fix that over on our hero academy let's make this implement i enumerable and that will be in the system collections namespace so this is just telling c sharp hey this hero academy can be enumerated somehow it doesn't know how to enumerate it but we'll get to that so this is giving us an error it's saying hey we need to implement the missing members so we'll see there's two words here i innumerable and i in numerator obviously seems very similar so this one tells c sharp it can be enumerated this one tells c sharp how to enumerate it all right so ienumerator is another interface and if we just go into it and these are the three functions required uh to uh use 4h properly all right so it's saying uh move next so move to the next index uh what the current item of the array is so that would be this here and if we ever want to reset it go back to the very first index of the array uh we can do that too so let's head back so we could fill out all those methods ourself right we could make our own uh enumerator which we will do later on but for now let's cheat we know that we just want to iterate over this list here and we know that list if we go into it we go down into i list we know that list already implements i enumerable all right or else we wouldn't be able to actually use a 4-h on a list so because we know we're iterating this let's just return the olympians dot get a numerator right because we know that our list implements enumerable which means we know that it's got this function so we're just going to return back out to the 4-h loop the lists enumerable so now we can say console.writeline name so take note this is an object that we've just created but now we can iterate over it as well as get access to the normal properties on the object so if we press play here we'll see zeus poseidon aphrodite okay cool so now you know what these two things are and roughly what they do i'm going to now just stop on this example and i'm going to show you how you're probably already using ienumerables and how you could potentially be screwing up uh what you think you might actually be doing i'll show you a few gotchas so i'm going to create a rand instance here a random instance as i know that i'm going to be using random a few a few times and then i'm going to create a list of numbers and i'm just going to do this by enumerable range 0 to 10 and i'm going to list it so basically what this is doing is it's just creating a list of numbers from 0 to 10. so now let's say we want to query some of these numbers and an easy way to do that is with link so let's say random numbers and we'll go numbers here and then we'll order them by number and then we'll use our rand next so we're just like randomly ordering these numbers and we're just going to take three of them so now we could simply for each var number in random numbers and we could say console.writeline number so let's press play and we'll say three random numbers seven one zero but what do you think is going to happen if we copy this for each loop and do this twice right so let's press play zero four one one six zero so this is this is actually giving us two sets of random numbers you may have looked at this and thought okay here are the random numbers right these are the random numbers that we've just returned from this query and now we're just going to loop over them two times but that's not the case so if we inspect what type this is we will say that it is a type i.enumerable okay so this is not storing the numbers this is not a variable with three numbers in it this is just a query and then when we get here we're running this query right and then once this for each loop finishes and it goes to this one we are then running the query again to uh get another set of random numbers if you're just learning this now you're probably thinking oh i need to go back through some of my code and find out if i'm actually performing enumerations multiple times now that doesn't mean that performing an enumeration multiple times is a bad thing okay sometimes it can be a very good thing but you need to just know that this is how it works now if you wanted to loop these number like the same set of numbers two times you could simply do this to list so now we're saying grab the numbers order them take three of them and then set them in stone make a list out of them actually cement them in this variable so now this variable is of type list right so now if we press play we will see 537 537 so now that's more predictable right so there's a few things you need to keep in mind here one two listing means you're grabbing all of the items and you're putting them all in memory in the heap right you're grabbing them or putting them all in memory which means once this goes out of scope the garbage collector is gonna have a little bit of extra uh allocated garbage that's gonna have to clean up whereas if you don't to list it we are actually only pulling one number at a time uh into the scope right now we're pulling one number into memory at a time which can obviously be a good or a bad thing for example say you have you've got uh you're gonna grab a million items right if you to list it you are putting one million items into a list and that could crash your app right it could it could absolutely crash your app uh and that in that scenario it might be better to leave it as an enumerable even if you're going to do it two times right because not not every query is going to return uh random results right this could you could just do a where statement here and you could iterate over them one at a time and then later on in your uh in your function you could also iterate them over again and it may not matter that you're doing the where statement two times uh the benefit is better the cpu cycles is better doing the where statement twice than uh pulling them all into memory right so just a uh direct example right let's let's say this is not random numbers let's just rename this and uh call it uh grid positions right so let's say that we're this is actually doing some kind of path finding and just pretend for a moment because they're not actually grid positions they're numbers but uh let's say instead of uh doing something random we're doing where and just to make this simulate a more expensive task like pathfinding for example let's just say we're uh delaying for 10 milliseconds and we'll wait for that and then we'll return true so i know this is a very pointless uh where statement i'm literally just returning true on everything but this will allow us to simulate a longer running task um and let's not just take three let's grab them all so then if we do this let's say four times right and this is disgusting so that's actually iterations equals four and then here we can do four each uh four iterations then we can just slap that in there and it's not so chunky cool so basically we're just doing we're looping that for the iterations but we're we're calling our enumeration four times now right and just to inspect this let's do uh a watch equals new stopwatch and we'll say watch dot oops watch dot start and then down here we'll do watch dot stop and then we'll do console right line elapsed watch dot elapsed milliseconds cool so we're now going to call this enumeration four times so if we press play here you can see how that slowly went down like that could be a pathfinding algorithm or it could be something even more expensive right you could be um you could be reading from disk or something so that took 600 milliseconds to loop this four times uh because we're actually redoing this pathfinding algorithm four times so instead of doing that we could to list it right and pull them all into memory just process it once pull them all into memory and now when we press play here it's like almost instant right nine milliseconds so from nine milliseconds all the way up to 600 milliseconds is the difference between two listening or not so it's it's really it's a judgment call do you want to allocate the extra memory of the two list right uh and just remember you're not just pulling all the items into memory you're also creating the the class itself right so that's also allocated uh which will have to be garbage collected sorry it's it you've got to weigh the pros and cons do you want to list it and allocate the memory or do you want to actually just keep running the uh enumerable over and over okay so you need to probably benchmark in what situation is best okay if it's a really quick uh if it's a really quick statement like like a simple wear statement or an order buyer that doesn't obviously have this stupid weight here it may be perfectly fine for you to just double enumerate instead of pulling into a list so yeah if you have just learned this i'm sure you are thinking about some code that you've written that you probably need to go back and refactor or at least take a look at to see if you're doing the right thing or if you're running multiple enumerations when you shouldn't when you shouldn't be uh or maybe multiple enumerations is fine you never know and just a little side note uh if we remove this to list so it's an innumerable again writer i've only just started using writer over the last two weeks but uh writer gives you little performance optimization tips like it's doing right here possible multiple and an enumeration right it's it's telling us hey you may not be realizing that you're actually enumerating this multiple times i'm going to warn you about it maybe you want to do something about it maybe you don't but it's it's small things like that that just make writer such a nice ide to use as well as obviously all the audio complete and just other suggestions it's just fantastic and before when i used to use a visual studio i also used to use resharper plugged into it which is also made by these guys and whenever i used to turn off resharper and just use basic visual studio my development speed just went way down resharper and writer just do so much for you that you don't even realize how much they're doing until you actually turn it off so i highly recommend the writer idea it's fantastic or getting resharper for visual studio honestly you won't look back uh it's fantastic anyway that's a side note hello so i'm happy to say that this video is sponsored by jetbrains the creator of resharper writer and a bunch of other beautiful tech they were lovely enough to give me five one year licenses to the writer ide which i'm using in this video all i'd like you to do to get your little fingers on it is comment down below what you think of this video and give me an idea of what you would like to see in a future video and also let me know that you would indeed like one of these licenses and in the near future i'll pick five of you and i'll send you a license and that's about it enjoy the rest of the video bye okay so now that you kind of know what the enumerations are doing i'm going to show you it uh actually a deep dive into it i'm going to write my own iron numerable and this will be a type int let's say get numbers and in here i'll do a for loop for 10 and now as you as you saw here we're pulling one item into the scope at at any one time right we're not grabbing them all we're just pulling one at a time and we can do that with iron numerables like this with the contextual yield keyword right so we're yield return and we'll just return i and if you're if you use unity you may actually recognize this yield as uh in co routines you will say yield new weight for seconds right or wait for the end of frame okay so instead of waiting for the end of frame we're just uh waiting for i to be returned so now down here we can say forage va num in get numbers and we'll just console write line the num and if we set a breakpoint here and here right and let's actually also console rightline and we'll say uh processed i and let's press debug so it's come down here it said alright get first get get numbers so now it's asking for our first number which will be zero obviously and then once once we uh go to the next step here it's now down back into our for each loop right and this number is now zero so we'll console log our number and now it's going to come back up here and ask for our next number so now i will be equal to 1 and it's going to now return that and it's just going to keep going through this pulling each item one by one right as soon as this number here goes out of scope it's now it's it's now going to be triggered by the garbage collector at some point in the future to say clean it up we don't need it anymore right and now this next number is now coming into scope one by one uh so you can see uh just how powerful this could possibly be right so we could actually we could actually say that this is going to return one million numbers okay but we could do something like this now but if num is more than five then uh break out of this loop right and we can do just something down here just to say hey uh right line we're done so even though that this innumerable could potentially return one million numbers because here we're breaking out of this statement to say if we're five we're done and we're leaving this will only process five or six sorry more than five so instead if you had a get numbers function right and it was a list right and you're saying new list here add to list and then return the list even if you're only wanting five on the outside this will still do one million numbers and then uh you will just end up using five right so plus that's pulling a million numbers into memory uh for you to only use five right so i always recommend if you've got some kind of function that returns a collection return an ienumerable yield it if you can and that way in one point of your application you might use the full million whereas in another application or another part of your application you you have the luxury of only using the five and we can actually use link on this so uh three numbers and we'll say get numbers dot take three and if you remember this is actually an innumerable as we're using link here sorry this is not storing the numbers once it gets down to here this still hasn't even executed this this function hasn't even called yet it will only actually call if we act on something and a super interesting part is if we console right line and we'll do like three numbers and we'll just say first right even though that we're saying that we want to take three here because we're only acting on the first one and we press play only the first one it's only going to iterate this one time just to grab the one that we need and then just like just ignore the rest it's not even going to do anything for the rest so you can write super performant code by using innumerables and you can reuse a lot of your code by having one function to return a ton of different uh amount of items okay instead of returning the list and grabbing all of them and then just taking some of the list so keep that in mind because it's a very good performance tip and a good code reusability tip now that we have uh kind of explored this a little bit i will just show you lastly how you can actually create your own iron numerator so right now we were lazy we just returned the list iron numerator right because we knew that this one already had it but let's create our own so let's create a class this will be an academy uh innumerator and this will implement ie numera iu numerator okay and as i showed you before this has three functions that we need to implement move next reset and current object we know that we're gonna need to return this academy enumerator here right instead of like just grabbing the list one so we actually need to know what we're enumerating okay we know that we're going to be enumerating this list so let's create a constructor here and in this constructor it's going to take a list of type string and this will be the names and then down here we'll say names equals names and then we will auto scaffold that and then we also need a index because we need to know which item of the array we need to keep handing to them so let's go private inch index and this will be equal to negative one and the reason i make it negative one is because do i have my no i don't ba academy whoops academy people's new hero academy then if we do 4h by hero in academy so when we first hit this it's going to immediately say get next move next right so if we made this zero it's actually going to make it one so then we're going to grab the second element of the array so you need to set it to negative one so that when you first say it it's going to be on on index zero this function here is actually going to do two things one it's going to iterate the index okay so move to the next index and two it needs to return a boolean and this boolean is saying is there another item in the array uh if this returns false it's going to get to here after iterating it a few times it will get to here it's going to return false so then it's going to just leave the forage loop and then continue execution down the rest of your application so we can easily say is there another item by saying is index less than our names.count if it is then yes true keep keep iterating um our reset function is very easy it's just index is going to be equal back to the start of the uh of the array so that we can enumerate it again so if you remember on our numbers array we were enumerating and then we would have another 4-h loop enumerating again so that's because the index would be reset to negative 1 each time and then our current is just uh the current version of our um of our object so that will be this here this is our current version of the iteration so here we can just say uh return uh names index and resharper is actually telling us that we can make it an expression body so that's nice and neat so now we've got our custom academy and numerator so here instead of returning the list one directly we can oh sorry doggies all over the place we can return our custom academy and numerator new academy enumerator and we've got a uh constructor here which takes in the name so let's send in our olympians and now here we can say console.writeline hero so now that this is going to be using our brand spanking new academy enumerator so let's just check to see if that works beautiful and if we put our mouse over this we'll see that this is actually a type of object okay it doesn't actually even know that this is uh we're enumerating strings here which means we can't actually perform link expressions here we can't do this for example we can't say where so to enable link expressions and to add proper typing to this we just need to change these to the generic types so instead of just i innumerable we'll have an eye interval of type string and this is saying hey you have got more methods that you need to implement and if we go into the generic version of this we'll see that there's the generic get enumerator right but then this generic uh eye innumerable is actually inheriting from the base iron numerable which also has like the basic get a numerator so that falls back onto us to have to actually implement both so let's remove this and then just implement those two [Music] so the good thing about this is we don't actually have to worry about this base one okay so this base one they've already pre-filled it to say hey just return whatever the generic version uh returns so we call in the base one by actually directly referencing the the interface and then calling the method directly so we can just ignore this one and just use this one to return our customer numerator so we'll return new academy and numerator and we'll send in our olympians and this is giving an error it's because our enumerator is actually also needs to be generic so let's make this a string and the generic version of iron numerator requires a little bit of different setup so i'm just going to remove this it needs a disposable which we won't actually use so i'm just going to remove the not implemented throw and uh re-implement the get so this is just still going to be names and then index and we'll make this the expression buddy cool and if we press play now we'll see that it returns our two names which are long enough cool bananas so uh ultimately you don't need to know this side like how to make your own innumerable how to make your own enumerator this is advanced stuff right but this stuff that i have taught over here i wish i didn't actually remove it all but yeah so this stuff knowing when to list your innumerables is vital okay you might have to go through your old code to check to see if you're actually enumerating things multiple times when you shouldn't be but this stuff is important so make sure you study this and know what you're doing uh it could save you know database rights or file system rights or pathfinding a million times and you shouldn't be happy when you shouldn't need to so that's it that's all i wanted to show you if you enjoyed the video subscribe let me know down below what other advanced topics you would like me to cover uh i've got a list but uh it's always good to have it keep growing so that i can just smash through them and uh that's it i'll see you in the next video bye you
Info
Channel: Tarodev
Views: 5,959
Rating: undefined out of 5
Keywords: c#, enumerable, ienumerable, ienumerator, enumerator, c# enumerable, implementing ienumerable and ienumerator, ienumerable in c#, c# tutorial, c# programming, c# basics, how to use ienumerable, c# foreach, c# loops, c# (programming language), c# programming language for beginners
Id: 2sqyd-qtHYg
Channel Id: undefined
Length: 25min 14sec (1514 seconds)
Published: Sun Nov 28 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.