c# - 为什么 `.Select(...).Last()` 被优化,而 `.Select(...).Last(...)` 没有被优化?

标签 c# .net linq

考虑以下枚举器:

var items = (new int[] { 1, 2, 3, 4, 5 }).Select(x =>
{
    Console.WriteLine($"inspect {x}");
    return x;
});

这会产生元素 [1, 2, 3, 4, 5] ,在它们被消耗时打印出来。

当我调用 Last此枚举器上的方法,它会触发仅访问单个元素的快速路径:

items.Last();
inspect 5

但是当我将回调传递给 Last 时, 它从头开始遍历整个列表:

items.Last(x => true);
inspect 1
inspect 2
inspect 3
inspect 4
inspect 5

查看 .NET Core 源代码,我发现:

另一方面:

这解释了如何未优化回调案例。但它没有解释原因

从概念上讲,如果至少有一个元素满足谓词(这在实践中很可能发生),则向后迭代可能允许提前退出循环。

实现起来似乎也不难:据我所见,只需要在 IPartition<T> 上添加一个方法即可.

缺乏优化也可能令人惊讶。由于这些重载共享相同的名称,人们可能会认为它们也以类似的方式进行了优化。 (至少我是这么想的。)

鉴于优化此案例的这些原因,为什么 LINQ 的作者选择不这样做?

最佳答案

Last()可以始终针对允许在恒定时间内访问集合的最后一个元素的集合进行优化 ( O(1) )。对于这些集合,您可以直接访问最后一个元素,而不是迭代所有集合并返回最后一个元素。

Conceptually, if at least one element satisfies the predicate (which is likely in practice), then iterating backward may allow for exiting the loop early.

这个假设对于 Last(Func<T,bool>) 的通用实现来说太牵强了。 .一般来说,您不能假设满足谓词的最后一个元素更接近集合的末尾。 优化 对您的示例(Last(x=>true))很有效,但对于每个这样的示例,都可能有一个相反的示例,其中优化 表现更差(Last(x=>false))。

关于c# - 为什么 `.Select(...).Last()` 被优化,而 `.Select(...).Last(...)` 没有被优化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54035356/

相关文章:

c# - 有没有办法避免在 Xamarin 中每个方法的 setter 中调用 NotifyChanged?

C# LINQ 转换查询语法扩展

c# - .NET PropertyInfos 的平等

c# - 使用事件记录是个好主意吗?

c# - 当 IDENTITY_INSERT 设置为 OFF 时,无法在表中插入标识列的显式值

c# - LINQ to Entities Union 抛出错误

c# - 在另一台机器上模拟本地用户

c# - DerivedClass 作为 ParentClass 类型变量的值

c# - 在azure函数V2中将TraceWriter替换为ILogger

c# - C# 中的空合并运算符 (??) 是线程安全的吗?