In the following test:
int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Func<int, int> boom = x => { Console.WriteLine(x); return x; };
var res = data.Select(boom).Skip(3).Take(4).ToList();
Console.WriteLine();
res.Select(boom).ToList();
The result is:
1
2
3
4
5
6
7
4
5
6
7
Essentially, I observed that in this example, Skip()
and Take()
work well, Skip()
is not as lazy as Take(). It seems that Skip()
still enumerates the items skipped, even though it does not return them.
The same applies if I do Take()
first. My best guess is that it needs to enumerate at least the first skip or take, in order to see where to go with the next one.
Why does it do this?
Skip()
and Take()
both operate on IEnumerable<>
.
IEnumerable<>
does not support skipping ahead -- it can only give you one item at a time. With this in mind, you can think of the Skip()
more as a filter -- it still touches all the items in the source sequence, but it filters out however many you tell it to. And importantly, it filters them out from getting to whatever is next, not for whatever is in front of it.
So, by doing this:
data.Select(boom).Skip(3)
You are performing boom()
on each item before they get to the Skip()
filter.
If you instead changed it to this, it would filter prior to the Select
and you would call boom()
on only the remaining items:
data.Skip(3).Take(4).Select(boom)
If you decompile Enumerable
, you will see the following implementation of Skip
:
while (count > 0 && e.MoveNext())
--count;
and the following implementation of Take
:
foreach (TSource source1 in source)
{
yield return source1;
if (--count == 0) break;
}
So, both of these LINQ methods actually enumerate through these items. The difference is whether an enumerated item will be placed in the resulting collection or not. That's how IEnumerable
does work.
They both iterate the collection. And then turnout with the final collection. Its just like a simple for loop
having a if else
condition in it.
Moreover you're selecting it first using boom
, it prints the item of collection.
By this example you can not tell if skip()
or take()
iterates the whole collection or not but the matter of fact, they do.
assuming i have understood your question correctly.
the boom func executes before the skip operation.
try to select data after skip and take , you will get the exact.
This behaviour maybe will be changed in the future, as can be seen here in this discussion about a Pull Request that optimizes the behaviour of the Skip method for sources that implement the IList interface.
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