Before going into Linq, here is again one of my pictures: Le Louvre by night, Paris
When using Linq to objects, you will quickly feel the need to pass some parameters from a method to another but it’s not so easy because each Linq method is not calling the following one. In a Linq sequence, each method is using the result computed by the previous one. So, local contexts are not visible from one method to another. The compiler is using two technical different ways to let parameters go out of a method.
As an example, let’s first see how the .SelectMany() method is working.
This very little example shows that s1 and s2 are both accessible in the select. It’s nice, but how does this work ? You must know that the ‘from’ statement of the Linq sugar syntax does not match any existing Linq method. Let’s see how we would have written this using the classical C# syntax.
Let’s focus on the SelectMany parameter:
SelectMany(Func<TSource, IEnumerable<TResult>> selector).
The method must return an IEnumerable<TResult>. In our example, we get it using values2.Select(). We are now touching the interesting point. The parameter of the .Select() is also a lambda “s2 => s1 + s2”. In Linq to object a lamda generates an anonymous method and anonymous methods can access their host scope. Here, s1 is visible inside values2.Select().
So the first solution to share parameters between methods is to nest them
like “.SelectMany(s => s.Select(s2 =>’s in accesible here’ ))”
instead of creating a sequence “source.Skip(‘scope1’).Take(‘scope2’)”
Now what if we would like to share a parameter between two following methods like Skip() and Take() ?
Let’s see how the “let” keyword works.
In the next example, I will start from a list of numbers stored as strings. I would like to get only numbers greater than 10 and order them by their value but keeping the result as an enumeration of string.
We can write it quickly this way:
This works fine but we all notice the ugly “Convert.ToInt32(s)” used twice on the same value. Thanks to the “let” keyword we can create a new parameter that will be accessible in all the query.
We can see that the “let” keyword solves this problem extremely easily. But once again the sugar syntax of Linq is really magic. As we always want to know the secrets of magic tricks, let’s try to get the same result without using the “let” keyword and we will find out what the C# compiler is really doing.
Let’s start by decomposing all the steps using the regular C# syntax.
I have chosen to decompose this way to show that the only place to share something between the Where() and the OrderBy() is the result.
Now we have to make a new parameter travel across the sequence in addition to the current result. The idea is to create a new type to group the current result and the new value. To achieve this easily we can just use a anonymous type.
We can of course write this in a single Linq query
which is the exact equivalent of what is compiled when using the ‘let’ keyword
Of course the Linq syntax is extremely short but it’s always nice to know what’s happening behind the scene.
I hope this sample can also help you to solve some other problems when playing with Linq.