在以下测试中:
int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; Funcboom = x => { Console.WriteLine(x); return x; }; var res = data.Select(boom).Skip(3).Take(4).ToList(); Console.WriteLine(); res.Select(boom).ToList();
结果是:
1 2 3 4 5 6 7 4 5 6 7
从本质上讲,我观察到在这个例子中,Skip()
并且Take()
工作得很好,Skip()
并不像Take()那样懒惰.似乎Skip()
仍然枚举跳过的项目,即使它没有返回它们.
如果我先做,这同样适用Take()
.我最好的猜测是,它需要至少枚举第一个跳过或跳过,以便查看下一个跳转到哪里.
为什么这样做?
Skip()
而Take()
这两种操作上IEnumerable<>
.
IEnumerable<>
不支持向前跳过 - 它一次只能给你一件物品.考虑到这一点,您可以将Skip()
更多内容视为过滤器 - 它仍会触及源序列中的所有项目,但它会过滤掉您告诉它的许多项目.而且重要的是,它会将它们从过去的任何东西中过滤掉,而不是用于它前面的任何东西.
所以,通过这样做:
data.Select(boom).Skip(3)
您boom()
在进入过滤器之前对每个项目执行操作Skip()
.
如果您改为将其更改为此,则会在之前进行过滤Select
,您只会调用boom()
其余项目:
data.Skip(3).Take(4).Select(boom)
如果您反编译Enumerable
,您将看到以下实现Skip
:
while (count > 0 && e.MoveNext()) --count;
以及以下实施Take
:
foreach (TSource source1 in source) { yield return source1; if (--count == 0) break; }
因此,这两种LINQ方法实际上都是通过这些项进行枚举的.不同之处在于枚举项是否将放置在生成的集合中.那是怎么回事IEnumerable
.