LINQ Skip仍然枚举跳过的项目

eri*_*osg 21 .net c# linq

在以下测试中:

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();
Run Code Online (Sandbox Code Playgroud)

结果是:

1
2
3
4
5
6
7

4
5
6
7
Run Code Online (Sandbox Code Playgroud)

从本质上讲,我观察到在这个例子中,Skip()并且Take()工作得很好,Skip()并不像Take()那样懒惰.似乎Skip()仍然枚举跳过的项目,即使它没有返回它们.

如果我先做,这同样适用Take().我最好的猜测是,它需要至少枚举第一个跳过或跳过,以便查看下一个跳转到哪里.

为什么这样做?

Cor*_*son 22

Skip()Take()这两种操作上IEnumerable<>.

IEnumerable<>不支持向前跳过 - 它一次只能给你一件物品.考虑到这一点,您可以将Skip()更多内容视为过滤器 - 它仍会触及源序列中的所有项目,但它会过滤掉您告诉它的许多项目.而且重要的是,它会将它们从过去的任何东西中过滤掉,而不是用于它前面的任何东西.

所以,通过这样做:

data.Select(boom).Skip(3)
Run Code Online (Sandbox Code Playgroud)

boom()在进入过滤器之前对每个项目执行操作Skip().

如果您改为将其更改为此,则会在之前进行过滤Select,您只会调用boom()其余项目:

data.Skip(3).Take(4).Select(boom)
Run Code Online (Sandbox Code Playgroud)


Yel*_*yev 5

如果您反编译Enumerable,您将看到以下实现Skip:

while (count > 0 && e.MoveNext())
  --count;
Run Code Online (Sandbox Code Playgroud)

以及以下实施Take:

foreach (TSource source1 in source)
{
  yield return source1;
  if (--count == 0) break;
}
Run Code Online (Sandbox Code Playgroud)

因此,这两种LINQ方法实际上都是通过这些项进行枚举的.不同之处在于枚举项是否将放置在生成的集合中.那是怎么回事IEnumerable.