在Joseph Albahari的《C#in a Nutshell》一书中,有这样的说法.具体实现如下:

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"将枚举我们的集合,因此在我们拥有集合的情况下.where(x=&gt;...).where(x=&gt;...).ToList()-枚举将发生3次:第一次在第一个WHERE中,第二次在第二个WHERE中,最后在ToList()中.但我在想,LINQ链的整个 idea 就是只列举一次. 我是不是搞错了,只会列举一次?请给我带路

推荐答案

看起来在这里使用"Foreach"将列举我们的集合,

它确实做到了look like,但实际情况并非如此.相反,关键字yield告诉编译器,它应该对方法执行transformation,这样它就会返回一个IEnumerable对象,该对象只有knows how来迭代集合,而不实际执行这项工作.在您实际使用并迭代这个返回的IEnumerable之前,根本不会发生枚举.

编译器转换的方式是stackable,因此即使在多次调用.Where()的情况下,就像问题中的collection.Where(x => ...).Where(x => ...).ToList()表达式一样,only one enumeration也会发生.您还可以混合使用其他LinQ操作,如.Select().Any().Aggregate()等.

因此,在问题的示例代码中,唯一的枚举作为对.ToList()的最终调用的一部分发生.

这可以使组成LINQ代码extremely efficient.缺点是每次使用它都会导致枚举对象的内存分配,所以当内存受限与CPU受限(不要忽略分配的GC成本)时,了解这一点是很有用的.通常,将它们中的一些组合起来是值得的,所以.Where(x => ...).Where(x => ...)变成了.Where(x => ... && ...).我仍然希望C#团队能想出一些神奇的"ValueEnumerable",就像他们对元组、任务和跨度所做的那样,可以减少这些分配.

最后,虽然有例外,但通常您应该尽可能多地使用avoid calling 100(或ToArray()).相反,对于方法参数、返回类型和变量声明,首选IEnumerable<T>而不是List<T>T[].在许多情况下,最终的foreach循环、数据绑定或其他机制强制执行最终的枚举就足够了,这样您实际上就不需要构造列表或数组,而这些其他 Select 是way more efficient,而不是实际构建(并为其分配内存)可能不需要的列表.

Csharp相关问答推荐

try 在C#中启动Tunnelbear.Exec

Plotly.NET访问互联网时出现异常

EF Core判断是否应用了AsSplitQuery()

. NET WireMock拒绝PostAsJsonAsync序列化

NumPy s fftn in C#with pythonnet'

从Blob存储中提取tar.gz文件并将提取结果上载到另一个Blob存储

如何在Parall.ForEachAsync中使用CancerationTokenSource

NET8 Maui&;iOS:AppCenter崩溃错误

在不添加不必要的尾随零的情况下本地化浮点型?

如何使用用于VS代码的.NET Maui扩展在我的iOS/Android设备或模拟器上进行调试?

在implementationFactory中避免循环依赖

升级后发出SWITCH语句

我是否应该注销全局异常处理程序

正在try 将自定义字体添加到我的控制台应用程序

如何在.NET Core 8中自定义标识用户模型

WPF如何获取有关从一个视图模型更改另一个视图模型的信息

从MudAutoComplete打开对话框,列表仍然可见

嵌套Blazor组件内的验证

将文本从剪贴板粘贴到RichTextBox时,新文本不会在RichTextBox ForeColor中着色

S,在.NET核心控制台应用程序中,AddScope和AddSingleton有什么不同?