Let's start with some source data to query:
int[] someData = { 1, 2 };
After running the following code, things work as I expect: a
contains 2 elements which boil down to 1
and 2
pulled from someData
.
List<IEnumerable<int>> a = new List<IEnumerable<int>>();
a.Add(someData.Where(n => n == 1));
a.Add(someData.Where(n => n == 2));
But this next code, which does exactly the same thing only in a loop, does not work as expected. When this code completes, b
contains 2 elements but they are both identical - pointing to 2
. On the 2nd loop, it modifies the first element of b
.
List<IEnumerable<int>> b = new List<IEnumerable<int>>();
for (int i = 1; i <= 2; ++i)
{
b.Add(someData.Where(n => n == i));
}
Why is this happening and how can I make the loop version behave like the first version?
Jon Skeet has a nice answer here
You need to assign i
to a temp
variable and use that in the Linq query
List<IEnumerable<int>> b = new List<IEnumerable<int>>();
for (int i = 1; i <= 2; ++i)
{
int temp = i;
b.Add(someData.Where(n => n == temp));
}
Your problem is lazy evaluation. You add an Enumerable to b
that represents someData.Where(n => n == i)
. This is evaluated whenever you look into an element of b
with the value i
has at that time.
You want to manifest the enumerable by calling ToArray()
or ToList()
on it.
for (int i = 1; i <= 2; ++i)
{
b.Add(someData.Where(n => n == i).ToArray());
}
Alternatively you can reduce the scope of the captured variable:
for (int i = 1; i <= 2; ++i)
{
int localI=i;
b.Add(someData.Where(n => n == localI));
}
Then you still have lazily evaluated enumerables(which shows when you modify someData
), but each has a different i
.
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