In my previous post I talked about LINQ and Deferred Execution … I decided to continue expanding on this topic by showing how with LINQ to Objects it’s not only the query that is not executed immediately. Lets start from the basics… When you have a query such as this one
Yet you still did not execute that query… i.e the query2 that we have in the above code still has no values in it, it only holds an object that can give the values that you would want to have (It’s just a plan of execution). This might sound strange yet true… WHY? It’s because of Iterators. This is very important!!! For a better understanding of how iterators work have a look at this post.
So basically the iterator (which is an object returned by the Where method) will give you one value from a list at a time ONLY when you request it. Once you request the value, then you can go ahead and process that value and request the next value from that list. We use this a lot for example when we create a foreach statement.
When you have a LINQ query the same thing would happen as if we are in a foreach statement. When you project a value from your query you did not process the whole list you only got one value at a time!!! For a better understanding of how this works I created a small demo app where I developed my own Where Iterator that prints in a console. Here is the code for this demo app.
Basically I am creating an extension method for IEnumerable<int> and called it MyOwnWhere that in essence does the same job of the Where extension method of .Net 3.5 with the difference that this one prints to console. The interesting part of all this, is the output in the console…
As you can see when you have a where query you will be going through the list only once. The MyOwnWhere method (by using the yield keyword which is the keyword for creating iterators) is emitting an integer at a time. Lets us try to write down the flow of execution…
– The foreach statement requests a value from the query
– The query will start by asking the MyOwnWhere for a value
– MyOwnWhere will start filtering the source list
– Once an item matching the lambda function passed is found we yield that value
– The body of the foreach can process that value
– Once the value is processed the foreach will request the next value and this will go back to step 1 until the MyOwnWhere will stop yield values.
So basically everything is happening “Just in time” when you request it. To summarize all this we can even say
– You request a value
– The Iterator yields that value
– You process the value and request another one
The only instances where this does not apply is when you have things like OrderBy, Group or Joins…. When you use such methods you will need to get all values before executing the order by for example. So the Where would have to be processed fully before the order by can continue…. So if we change our code to this
than the result would be this
So here we had to process the whole list before we can order the result… No more just in time over here🙂
I hope that this post helps a bit more you guys to understand how LINQ to Objects actually works below the covers🙂
Happy new year to everyone🙂