C# LINQ可枚举策略比较:Any、Count、SingleOrDefault
我们在的时候进行了一些反思,发现了迭代C# LINQ可枚举策略比较:Any、Count、SingleOrDefault,c#,linq,optimization,enumerable,reference-source,C#,Linq,Optimization,Enumerable,Reference Source,我们在的时候进行了一些反思,发现了迭代IEnumerable的不同策略,从而为我们提供了一些关于它的信息 任何 公共静态bool Any( 此IEnumerable源,Func谓词) { if(source==null)抛出错误.ArgumentNull(“source”); if(predicate==null)抛出Error.ArgumentNull(“predicate”); foreach(源中的TSource元素){ if(谓词(元素))返回true; } 返回false; } 公共静
IEnumerable
的不同策略,从而为我们提供了一些关于它的信息
任何
公共静态bool Any(
此IEnumerable源,Func谓词)
{
if(source==null)抛出错误.ArgumentNull(“source”);
if(predicate==null)抛出Error.ArgumentNull(“predicate”);
foreach(源中的TSource元素){
if(谓词(元素))返回true;
}
返回false;
}
公共静态bool Any(此IEnumerable源)
{
if(source==null)抛出错误.ArgumentNull(“source”);
使用(IEnumerator e=source.GetEnumerator()){
如果(e.MoveNext())返回true;
}
返回false;
}
计数
公共静态整数计数(此IEnumerable源)
{
if(source==null)抛出错误.ArgumentNull(“source”);
ICollection collectionoft=源作为ICollection;
如果(collectionoft!=null)返回collectionoft.Count;
ICollection collection=源作为ICollection;
if(collection!=null)返回collection.Count;
整数计数=0;
使用(IEnumerator e=source.GetEnumerator()){
检查{
而(e.MoveNext())count++;
}
}
返回计数;
}
单缺省
publicstatictsourcesingleordefault(
此IEnumerable源,Func谓词)
{
if(source==null)抛出错误.ArgumentNull(“source”);
if(predicate==null)抛出Error.ArgumentNull(“predicate”);
TSource result=默认值(TSource);
长计数=0;
foreach(源中的TSource元素){
if(谓词(元素)){
结果=元素;
选中{count++;}
}
}
开关(计数){
案例0:返回默认值(TSource);
案例1:返回结果;
}
抛出错误。超过一个匹配();
}
如您所见,使用了3种不同的策略
- Any:迭代枚举数,并提前退出
- Count:迭代枚举数,除非ICollection调用Count
- SingleOrDefault:迭代枚举数,不提前退出
- “Any()”也可以针对ICollection进行优化
- “SingleOrDefault”也可以进行优化,以便提前退出
- 所有方法都不关心IReadOnlyCollection,即使它还有一个属性“Count”
- 每种策略的优点和缺点是什么
- LINQ的实现是这样的,有什么好的理由吗
- 好吧,前两种方法是不言自明的,不是吗?它们的优化方式是尽快停止,并检查类型是否具有
Count
属性以避免循环
但是with谓词确实有一个奇怪的实现。它可能会在第二个匹配项处停止,因为很明显必须抛出invalidoOperationException
(Single…
,而不是First…
确保最多有一个项)。相反,它会检查每一项并统计匹配项。
服务器具有这种优化
所以问题是:到底是什么?而且这似乎不会被修复,因为这只会降低出错情况下的性能。真不敢相信
顺便说一句,同样的错误也存在于。如果你传递
Any
一个谓词
,你必须查看这些项以确定是否至少有一个匹配,所以我不知道如何为ICollection
优化它,除非你只是在谈论使用for
循环。在任何情况下,这对SO来说都是过于基于观点的。@steve16351但它可能在看到第二个匹配项时立即退出并出现错误。@juharr如果您对any(谓词)的看法是正确的,我还将包括any(),它可以针对ICollection进行优化。您正在查看.NET Framework源代码。NET核心有一些.NET Framework没有的优化。例如,SingleOrDefault
和一个谓词在找到第二个匹配项时会发生短路,这不是一个急切与懒惰的问题。调用GetEnumerator
可能需要创建一个新对象,但我猜任何额外的开销都不足以证明尝试强制转换的合理性。SingleOrDefault是最令人担忧的。谢谢你的澄清。计数检查可能是一个有争议的任何优化。事实上,他们没有费心检查任何()中的ICollection.Count,可能是因为它在这里不是一个优化。@dfhwze:优化将是如此微不足道,以至于它不是麦汁。还要考虑的是,有人创建了一个类型,实现了<代码> IcLogEng/<代码>的方式,调用<代码>计数>代码>属性将做大量工作。那么这个“优化”就可以做到这一点opposite@TimSchmelter尽管这违背了合同/一般期望,即房产只能做轻量工作,理想情况下可以立即归还。如果有人编写的代码中Count做了大量的工作,那是他们自己的问题,做了一些非常错误的事情。@ckuri可能是这样,但事实是属性经常起作用,而方法通常不起作用。微软不应该编写不必要的代码来捕获写得不好的代码,但这样做会带来尽可能少的麻烦。
public static bool Any<TSource>(
this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null) throw Error.ArgumentNull("source");
if (predicate == null) throw Error.ArgumentNull("predicate");
foreach (TSource element in source) {
if (predicate(element)) return true;
}
return false;
}
public static bool Any<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw Error.ArgumentNull("source");
using (IEnumerator<TSource> e = source.GetEnumerator()) {
if (e.MoveNext()) return true;
}
return false;
}
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw Error.ArgumentNull("source");
ICollection<TSource> collectionoft = source as ICollection<TSource>;
if (collectionoft != null) return collectionoft.Count;
ICollection collection = source as ICollection;
if (collection != null) return collection.Count;
int count = 0;
using (IEnumerator<TSource> e = source.GetEnumerator()) {
checked {
while (e.MoveNext()) count++;
}
}
return count;
}
public static TSource SingleOrDefault<TSource>(
this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null) throw Error.ArgumentNull("source");
if (predicate == null) throw Error.ArgumentNull("predicate");
TSource result = default(TSource);
long count = 0;
foreach (TSource element in source) {
if (predicate(element)) {
result = element;
checked { count++; }
}
}
switch (count) {
case 0: return default(TSource);
case 1: return result;
}
throw Error.MoreThanOneMatch();
}