更具体地说:Linq扩展方法Any(IEnumerable collection, Func predicate)
将停止检查集合的所有剩余元素,一旦谓词为项产生true ?
因为我不想花太多时间去弄清楚我是否需要做那些真正昂贵的部分:
if(lotsOfItems.Any(x => x.ID == target.ID))
//do expensive calculation here
因此,如果Any
总是检查源中的所有项目,这可能最终会浪费时间,而不是直接使用:
var candidate = lotsOfItems.FirstOrDefault(x => x.ID == target.ID)
if(candicate != null)
//do expensive calculation here
因为我很确定FirstOrDefault
一旦得到结果就返回,并且如果在集合中没有找到合适的条目,则只继续遍历整个Enumerable
。
有没有人有关于Any
内部工作的信息,或者有人可以为这种决定提出解决方案吗?
另外,一位同事建议这样做:
if(!lotsOfItems.All(x => x.ID != target.ID))
因为这应该在条件第一次返回false时停止,但我不确定这一点,所以如果有人能对此有所了解,我将不胜感激。
正如我们从源代码中看到的,Yes:
internal static bool Any<T>(this IEnumerable<T> source, Func<T, bool> predicate) {
foreach (T element in source) {
if (predicate(element)) {
return true; // Attention to this line
}
}
return false;
}
Any()
是确定序列中任意元素是否满足LINQ条件的最有效方法。
also:一个同事建议用
如果(! lotsOfItems。所有(x => x.ID != target.ID)),因为这应该是一旦条件第一次返回false,停止,但我不是当然,如果有人能解释一下这个问题非常感谢:>]
All()
判断一个序列的所有元素是否满足一个条件。因此,一旦可以确定结果,就停止对source的枚举。
额外注意:
如果您正在使用Linq to对象,则上述情况是正确的。如果您正在使用Linq to Database,那么它将创建一个查询,并将对数据库执行查询。
您可以自己测试:https://ideone.com/nIDKxr
public static IEnumerable<int> Tester()
{
yield return 1;
yield return 2;
throw new Exception();
}
static void Main(string[] args)
{
Console.WriteLine(Tester().Any(x => x == 1));
Console.WriteLine(Tester().Any(x => x == 2));
try
{
Console.WriteLine(Tester().Any(x => x == 3));
}
catch
{
Console.WriteLine("Error here");
}
}
是的,它有:-)
also:一个同事建议用
如果(! lotsOfItems。所有(x =比;= target.ID))
因为这应该在条件第一次返回false时停止,但我不确定,所以如果有人能对此有所了解,我将不胜感激:>]
使用相同的推理,即使其中一个元素返回false, All()
也可以继续:-)不,甚至All()
也被正确编程:-)
它做任何最快的方法来做它必须做的事情。
当在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)
倾向于与Any(predicate)
相当。
Single
是这个
更新:以下内容从现在起不再适用于。net Core,它已经改变了Single
的实现。
需要注意的是,在link -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-object,但.Where(predicate).Single()
比Single(predicate)
更安全。
Any在第一次匹配时停止。
我不知道文档是否保证了这一点,但由于兼容性原因,这种行为现在一直有效地修复了。
是,它在满足谓词一次时停止。下面是通过RedGate反射器的代码:
[__DynamicallyInvokable]
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 local in source)
{
if (predicate(local))
{
return true;
}
}
return false;
}