Reputation: 14320
So it is my understanding that LINQ does not execute everything immediately, it simply stores information to get at the data. So if you do a Where
, nothing actually happens to the list, you just get an IEnumerable
that has the information it needs to become the list.
One can 'collapse' this information to an actual list by calling ToList
.
Now I am wondering, why would the LINQ team implement it like this? It is pretty easy to add a List
at each step (or a Dictionary
) to cache the results that have already been calculated, so I guess there must be a good reason.
This can be checked by this code:
var list = Enumerable.Range(1, 10).Where(i => {
Console.WriteLine("Enumerating: " + i);
return true;
});
var list2 = list.All(i => {
return true;
});
var list3 = list.Any(i => {
return false;
});
If the cache were there, it would only output the Enumerating: i
once for each number, it would get the items from the cache the second time.
Edit: Additional question, why does LINQ not include a cache option? Like .Cache()
to cache the result of the previous enumerable?
Upvotes: 2
Views: 367
Reputation: 62119
Because it makes no sense, and if you would think about all the cases where it makes no sense you would not ask it. This is not so much a "does it sometimes make sense" question as a "are there side effects that make it bad". Next time you evaluate something like this, think about the negatives:
Stuff like that makes is very hard to sensibly take away the choice from the developer. Want a buffer, make one (easily). But the side effects would be bad.
Upvotes: 6
Reputation: 157038
It is pretty easy to add a List at each step
Yes, and very memory intensive. What if the data set contains 2 GB of data in total, and you have to store that in memory at once. If you iterate over it and fetch it in parts, you don't have a lot of memory pressure. When serializing 2 GB to memory you do, not to imagine what happens if every step will do the same...
You know your code and your specific use case, so only you as a developer can determine when it is useful to split off some iterations to memory. The framework can't know that.
Upvotes: 6