LINQ 中的 .Where 实际上是如何工作的?

问题描述 投票:0回答:1

在 Joseph Albahari 的《C# in a Nutshell》一书中,据说 .Where 的实现如下:

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource,bool> predicate)
{
    foreach (TSource element in source)
        if (predicate (element))
            yield return element;
}

但对我来说,这里使用“foreach”似乎会枚举我们的集合,所以在我们有 collection.Where(x => ...).Where(x => ...).ToList() 的情况下 -枚举将发生 3 次:第一次在第一个Where中,第二次在第二个Where中,最后一次在ToList()中。但我在想,LINQ 链的整个思想就是只枚举一次。 我是否弄错了,它只会被枚举一次?请指导我

c# linq ienumerable
1个回答
2
投票

看起来在这里使用“foreach”将枚举我们的集合,

它确实看起来像这样,但实际情况并非如此。相反,yield

关键字告诉编译器应该对方法执行
转换,以便它返回一个IEnumerable
对象,该对象仅
知道如何迭代集合,而无需实际执行该工作。在您实际使用并迭代此返回的 IEnumerable 之前,根本不会发生枚举。

编译器转换以

可堆栈的方式发生,因此即使多次调用.Where()

(如问题中的
collection.Where(x => ...).Where(x => ...).ToList()
表达式),
仅发生一次枚举。您也可以混合使用其他 linq 操作,例如 .Select()
.Any()
.Aggregate()
 等。

这可以使编写 linq 代码

极其高效。缺点是每次使用此方法都会导致枚举对象的内存分配,因此了解内存受限与 CPU 受限的情况很有用,并且通常值得将其中一些结合起来,因此 .Where(x => ...).Where(x => )

 改为
.Where(x => ... && ...)

最后,虽然也有例外,但您应该

尽可能避免调用 ToList()

(或 ToArray()
)。相反,对于方法参数、返回类型和变量声明,请使用 
IEnumerable<T>
 而不是 
List<T>
T[]
。在许多情况下,最终的
foreach
循环、数据绑定或其他机制足以强制最终枚举,这样您实际上不需要构造列表或数组,并且这些其他选项
更有效 而不是实际构建一个可能不需要的列表。

© www.soinside.com 2019 - 2024. All rights reserved.