Ste*_*fan 3 c# linq performance
首先,我不确定这个问题是否适合这个社区.如果没有请告诉我在哪里移动它:)
所以,我经常发现自己经常写这样的Linq表达式:
var xy = someSource.Where(x => x.Property == value).Select(x => new Y(x));
Run Code Online (Sandbox Code Playgroud)
在重构我的代码时,我认为这实际上枚举了我的源代码两次,所以我写了这个小扩展(实际上没什么特别的):
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以我可以用我的查询替换
var xy = someSource.SelectWhere(x => x.Property == value, x => new Y(x));
Run Code Online (Sandbox Code Playgroud)
当然,这只会有明显的性能提升(如果它确实存在),如果源可枚举大或每个"移动下一个"需要很长时间.
我的问题是:这是否真的提高了性能(一点点),是否值得进行此扩展?
LINQ和enumerables在设计上是懒惰的,这意味着它们只在实际请求结果集合中的项时迭代源集合.
因此,从您的xy遗嘱中获取一个元素只会从原始中获取项目,someSource直到它找到与您的Where表达式匹配的项目,然后将其直接传递给Select转换器.它实际上一次只评估一个项目.你必须想象一下这里的管道:
从xyiterable 请求一个项使得xy迭代器请求成为一个来自Selectiterable 的项,这使得迭代器从迭代中请求一个项,Where迭代器将一次从原始的可迭代项中请求项.
这使得枚举变得非常懒(这通常对性能非常有利),但也增加了一些管理链中各种迭代器的开销:链越长(操作越多),开销就越有影响.
通常,几乎没有理由优化这些操作.LINQ的速度非常快,不会很快成为瓶颈,除非您确实将其视为应用程序的瓶颈(通过分析代码),否则您不应该投入精力来优化代码.对于LINQ来说尤其如此,因为LINQ的目标是非常易读且易于理解.
当然,你可以为常见的LINQ操作组合进行额外的扩展,但是我会说你很可能不会注意到从它中删除一个迭代器会带来的性能提升.
如果你真的在编写性能关键的东西,并且你知道你的LINQ表达式是瓶颈,但是你想继续能够出于可读性的原因编写LINQ表达式,你可以看一下roslyn-linq-rewrite.它是一个基于Roslyn的工具,可以在编译时将您的LINQ表达式重写为过程代码,使它们非常高效,而不会牺牲LINQ表达式给您的轻松.