它确实不管是做什么,它必须做的最快捷的方式。
当上一个IEnumerable
使用,这将是沿着线:
foreach(var item in source)
if(predicate(item))
return true;
return false;
或为不带谓词的变体:
using(var en = source.GetEnumerator())
return en.MoveNext();
当数据库它会针对其运行有点像
SELECT EXISTS(SELECT null FROM [some table] WHERE [some where clause])
依此类推。如何执行将依次取决于可用于实现WHERE子句的索引,因此它可以是快速索引查找,找到第一个匹配时中止的全表扫描,或索引查找,然后是部分表扫描,然后中止首先找到匹配,取决于那个。
其他的Linq提供者还有其他的实现,但通常负责人会试图至少合理地有效。
总而言之,您可以依赖它至少比调用FirstOrDefault
更有效率,因为FirstOrDefault
使用类似的方法,但必须返回完整的对象(可能构造它)。同样,!All(inversePredicate)
往往与this answer的Any(predicate)
相当。
Single
是一个例外
更新:从这点以下上不再适用于.NET的核心,它改变的Single
实施。
重要的是要注意,在linq-to对象的情况下,带有谓词的Single
和SingleOrDefault
的超负荷不会停止在发现的故障上。尽管对Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
明显的办法是这样的:
public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
/* do null checks */
using(var en = source.GetEnumerator())
while(en.MoveNext())
{
var val = en.Current;
if(predicate(val))
{
while(en.MoveNext())
if(predicate(en.Current))
throw new InvalidOperationException("too many matching items");
return val;
}
}
throw new InvalidOperationException("no matching items");
}
实际的实现是一样的东西:现在
public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
/* do null checks */
var result = default(TSource);
long tally = 0;
for(var item in source)
if(predicate(item))
{
result = item;
checked{++tally;}
}
switch(tally)
{
case 0:
throw new InvalidOperationException("no matching items");
case 1:
return result;
default:
throw new InvalidOperationException("too many matching items");
}
}
,而成功的Single
将需要扫描的一切,这可能意味着一个不成功的Single
比需要的要慢得多(甚至可能会抛出未记录的错误),并且如果意外重复的原因是将项目复制到序列中的错误 - 并因此使其远大于它应该的大小,则应该有所帮助的Single
你发现这个问题现在正在拖延。
SingleOrDefault
具有相同的问题。
这只适用于linq-to-objects,但它做.Where(predicate).Single()
而不是Single(predicate)
更安全。
[Enumerable.Any()']的文档(https://msdn.microsoft.com/en-us/library/vstudio/bb337697%28v=vs.100%29.aspx)明确指出了答案对此:*只要确定结果,源的枚举就会停止。* –