JeremyBytes - Lambdas & LINQ in C# - Bonus: Captured Variables & for Loops

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
this is Jeremy Clarke of Jeremy bytes calm and we're going to talk a little bit more about lambda expressions today specifically how we can use captured variables with for loops in part one of the series we looked at captured variables and one important feature is the value of the variable is the value at the time that it's used not the value at the time that its captured now normally this isn't a problem but there is one situation where we can get into trouble and that's if we try to capture the indexer of a for loop so today we'll look at a little bit of code to see that problem and we'll see a very simple solution here in Visual Studio you'll notice that we have a new bonus folder in our solution if you download the code from github you will see this and we'll be looking at the captured variables project today now this is just a console application and we see that we have some pretty simple code so far right now all we're doing is calling the get people method on our people object in order to get a list of person if we click into this class we see that it is pretty simple in fact our get people method is just returning a hard-coded collection of person objects now if we look at the person class this will look similar to the person objects we've been dealing with so far but this does have an additional property an ID field this will give us a primary key that we can deal with and more importantly when we output these items to the screen the ID property will make it really easy to distinguish our items so if we go back to our program we'll see we have an output person method this is a pretty simple method that just takes a list of person and an integer and it just uses that integer to index into the list and call the to string method on the selected item now the example that we have here is a bit contrived but it will get our point across when we're using captured variables with for loops so let's go ahead and create a method that will call this output person method so for this I'm just going to create a private static method that will return void and we're going to call this bad capture since this is where we're going to start off now this will take a list of person as a parameter and we'll call this people and what we'll do inside here is use a for loop now I love code snip so I'm just gonna type for and hit tab twice and we'll see Visual Studio has stubbed this out for us I'm going to tab over to the length and for the length of this we're going to say people dot count so this will be the number of items that are in our list and we're saying less than count since we have a zero based index or now we want to show capturing this indexer the I so I'm going to say task dot run and then inside here we want an action now an action is simply a delegate and in this case it will take no parameters and return void as we saw earlier we can create a land expression with no parameters by just putting some empty parentheses and then we'll add the goes to operator and then we're going to call our output person method now for this we do have two parameters so we'll use people for our list and for the indexer we'll use I and what we're doing in this case is we're capturing two variables inside our lambda expression we're capturing people which is a parameter that's coming into our bad capture method and we're also capturing I the indexer of our for loop now what I would expect is to get an incremental list of all of the items in this people collection but as we'll see that's not exactly what we'll get so we'll call bad capture up here and we'll pass in the people collection and then let's just go ahead and run our application now as you can see we got a runtime exception and it's an argument out of range exception and we can see that the message says index was out of range must be non-negative and less than the size of the collection now this seems very strange so let's go ahead and put in a little bit of air handling so that we can see what value were actually getting out for index now like I said I love code snippets and there's also this really cool keyboard shortcut so in this case I'm going to highlight the one line we have I'm going to use the keyboard shortcut ctrl K ctrl s which is surround with and then I'm going to type in the try snippet now if I hit tab notice that it just surrounded the line that I highlighted in a try-catch block I really love this because I like to have visual studio do as much of the work for me as possible now the exception that we're looking for is an argument out of range exception so let's go ahead and choose that and then we'll just do a console dot write line to output a message that we can see so we'll just say argument out of range and then we'll put in our actual value and then we don't want to re throw this exception we just want to go ahead and handle this here now let's run our application again and this shows us what our problem is notice that the actual value of the indexer that we captured is seven and also notice that it's the same for all seven calls of this method and that's something that we don't expect by just looking at this code when we capture the indexer we would expect that it would have different values for each call to output person but as we can see it's all the same and that's because the value of the captured variable is the value at the time it's used not the value at the time it's captured so by the time it's used this for loop has completed which means the value of I is people dot count which happens to be seven since we have seven items in our collection but that's out of range since we have a zero based index or on our list so this is the problem and you might say well how do I get around this it's actually a pretty easy solution but before I get there I want to do a little bit of housekeeping since we're using tasks I want to make sure that all of the tasks have completed running before I go on to the next step so for this I'm going to create a local variable called tasks and this is going to be an array of task items and this is going to be the same size as our collection now the reason I have this is I want to save off each of these tasks if we look at tasks run notice that it returns a task so let's go ahead and save a copy of this so we'll just say tasks I equals task dot run and this will save off that particular task that we just created now we don't have to worry about the index or in this case because we're not capturing it we're just using it and at the very bottom this method I'm just going to use the weight all method on the task object and this will wait until all of the tasks in our local array have completed before this method continues executing and if we run the application we'll see we get exactly the same output that we had before at this point so let's go ahead and create a method that captures the variables correctly so for this will just say private static void we'll call this one good capture and this will also take a list of person and this will also have a for loop so again we'll just use our snippets and we'll say people dot count for the length now what I'm going to do inside my for loop is create a local variable called captured index and this will just be set to I the indexer that's coming from our for loop and then this is the actual value that we'll capture in our land expression so we'll say task dot run we'll use our empty parentheses goes to output person and we'll say people and captured index now the difference here is we're not capturing I the index are from the for loop instead we're capturing this captured index variable this is a local variable that's scoped for a single iteration of this for loop so what this means is each time through the for loop we create a new captured index variable and then our lambda expression captures that one item so we end up with seven different variables and each of these will have the value we expect since we're not changing the value after we set it so now let's call this method and again this is called good capture so now if we run our application we get the output that we expect so we do see our different values but notice they're in no particular order so we have two five one seven three six four and if we run our application again we'll see that they come out in a different order that's just because of the way the scheduler works when we're dealing with tasks now like we did before I'm going to go ahead and just add a little bit of housekeeping code so that these two methods look the same so we'll create a variable called tasks and we'll set this to a new array and then each time through the loop we'll make sure that we go ahead and save off the tasks that we're creating and then at the bottom of the method we'll make sure that we wait for everything to finish before we continue and when we say wait all it just makes sure that when we're doing our output all of our bad values are grouped together and all of our good values are grouped together so just to review when we capture a variable the value is the value at the time it's used not the value at the time that it's captured in most situations we don't run into a problem but if we do try to capture the indexer of a for loop we'll end up with the final value of that indexer and not the intermediate values that we're expecting but there is an easy solution to this all we need to do is create a variable that's local to that iteration and save off the value of the indexer the result is that instead of capturing a single variable we're actually capturing seven different variables and each of those has the value that we expect because the value doesn't change after we capture it so as we've seen captured variables and for loops don't go very well together if we're not paying attention we might get values that we don't expect but as we've seen there is a very easy solution to the problem all we need to do is keep in mind that the value of a captured variable is the value at the time it's used not the value at the time it's captured to get more information and links to the code on github just follow the links for this video or visit w-w-w links to this and other videos in the series thanks for joining me and we'll see you next time
Info
Channel: Jeremy Clark
Views: 8,050
Rating: undefined out of 5
Keywords: C# (Programming Language), Language Integrated Query (Programming Language), Lambda Expression, Captured Variable
Id: T1onS_dCheY
Channel Id: undefined
Length: 10min 36sec (636 seconds)
Published: Mon Apr 20 2015
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.