Is this fair to use a single lambda expression variable in multiple chained calls? For example:
MyList.Where(i => i.ID > 20).OrderBy(i => i.Name);
So does i
used in Where()
remain independent of the i
used in OrderBy()
or can they have some hidden side-effects on each other, so that I must used different variable for each? Also, does your answer hold for VB.NET too?
I'm asking this because I have read in a slightly different context that I should not use foreach variable in LINQ queries directly and instead make a local copy of the variable inside the loop. Is there some similar effect hidden in the above code too?
They are completely independent. Actually, each time you declare a lambda you declare also range variables, which are local to this labda expression. So the i
in the Where(i => i.ID > 20)
is completely different from the i
in OrderBy(i => i.Name)
. In the first case i
refers to the random element of MyList
, and then i
refers to the random element of the sequence that comes from the Where
clause, which would be the sequence of all the elements of MyList
that have an ID>20
.
To elaborate a bit more, this is fine:
MyList.Where(i => i.ID > 20).OrderBy(i => i.Name);
Those two variables called i
are completely separate and only exist within the context of their respective lambda expressions. However, this is not:
int i = 0;
MyList.Where(i => i.ID > 20).OrderBy(i => i.Name);
Now those i
's in your lambdas conflict with the i
defined in the parent scope.
The issue with foreach
is a little more subtle and slightly tangential to your original question. If you have this:
foreach (var foo in fooList)
{
var filteredList = MyList.Where(i => i.ID > foo.Id).OrderBy(i => i.Name);
}
The problem here is because LINQ uses deferred execution and references the loop variable foo
so it will create a closure to include foo
. The problem is, it doesn't copy foo
it actually has a reference to the variable. So when you finally execute your lambda's by iterating it, or:
var bar = filteredList.ToList();
The value of foo
in your Where
lambda will be the value of foo
right now, not the value when the lambda was declared. So foo
will always be the last item from fooList
. Copying the variable fixes this problem because now it will close over that distinct variable (that only exists for that one iteration of the loop) rather than the loop variable.
foreach (var foo in fooList)
{
var copy = foo;
var filteredList = MyList.Where(i => i.ID > copy.Id).OrderBy(i => i.Name);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With