自定义Linq扩展的性能差异/改进

时间:2016-10-11 09:25:38

标签: c# performance linq

首先,我不确定这个问题是否适合这个社区。如果没有,请告诉我在哪里移动它:)

所以,我经常发现自己经常写这样的Linq表达式:

var xy = someSource.Where(x => x.Property == value).Select(x => new Y(x));

在重构我的代码时,我认为这实际上列举了我的源代码两次,所以我写了这个小扩展(实际上没什么特别的):

    public static IEnumerable<TResult> SelectWhere<TIn, TResult>(this IEnumerable<TIn> source,
        Func<TIn, bool> predicate, Func<TIn, TResult> selector)
    {
        foreach (var item in source)
        {
            if (predicate(item))
            {
                yield return selector(item);
            }
        }
    } 

所以我可以用

替换我的查询
var xy = someSource.SelectWhere(x => x.Property == value, x => new Y(x));

当然,这只会有明显的性能提升(如果它确实存在),如果源可枚举大或每个“移动下一个”需要很长时间......

我的问题是:这是否真的能提高性能(一点点),是否值得进行此扩展?

2 个答案:

答案 0 :(得分:5)

LINQ和enumerables在设计上是懒惰的,这意味着它们只在实际请求结果集合中的项目时迭代源集合。

因此,从xy获取一个元素只会从原始someSource中获取项目,直到找到与您的Where表达式匹配的项目,然后将其直接传递给Select变形金刚。它实际上一次只评估一个项目。你必须想象一下这里的管道:

xy iterable请求一个项使得xy迭代器请求来自Select iterable的一个项,这使得迭代器从Where迭代中请求一个项,迭代器将一次请求来自原始可迭代项目的项目。

这使得enumerables非常懒(这通常对性能非常有利)但是也增加了一些管理链中各种迭代器的开销:链越长(你拥有的操作越多),开销就越大。有

一般来说,几乎没有理由优化这些操作。 LINQ的速度非常快,不会很快成为瓶颈,除非您确实将其视为应用程序的瓶颈(通过分析代码),否则您不应该投入精力来优化代码。对于LINQ来说尤其如此,因为LINQ的目标是非常易读且易于理解。

当然,您可以为常见的LINQ操作组合进行额外的扩展,但是我会说您很可能不会注意到从中删除一个迭代器会带来的性能提升。

如果你真的在编写性能关键的东西,并且你知道你的LINQ表达式是瓶颈,但是你想继续能够出于可读性的原因编写LINQ表达式,你可以看一下roslyn-linq-rewrite 。它是一个基于Roslyn的工具,可以在编译时将LINQ表达式重写为过程代码,使其超高效,而不会牺牲LINQ表达式为您提供的简便性。

答案 1 :(得分:1)

Linq WhereSelect正在一个枚举中处理。

这导致一个枚举:

var xy = someSource.Where(x => x.Property == value).Select(x => new Y(x));

因此无需优化。