2015-05-15 39 views
4

更具体地说:一旦谓词对于一个项目而言是正确的,Linq扩展方法Any(IEnumerable collection, Func predicate)将停止检查集合中的所有剩余元素吗?是否有任何()停止成功?

因为我不想花太多的时间搞清楚,如果我需要做的真的昂贵的部件都:

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)) 

,因为这是应该停止一旦条件返回的第一次假,但我没把握,因此,如果任何人都可以我们也会对此表示赞赏。

+3

[Enumerable.Any()']的文档(https://msdn.microsoft.com/en-us/library/vstudio/bb337697%28v=vs.100%29.aspx)明确指出了答案对此:*只要确定结果,源的枚举就会停止。* –

回答

8

当我们从source code看到,

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条件的最有效方式。

也:一个同事建议沿

线的东西,如果因为这是应该 停止一旦条件(lotsOfItems.All(X => x.ID = target.ID)!)返回的第一次假但我不 肯定上,因此,如果任何人都可以在此阐明一些以及它 将理解:>]

All()确定序列中的所有元素是否满足一个条件。因此,只要结果可以确定,源的枚举就会停止。

附加说明:
如果你正在使用LINQ to对象上面是真实的。如果你正在使用Linq到数据库,那么它将创建一个查询并且将针对数据库执行它。

1

任何在第一场比赛时停止。所有站在第一次不匹配。

我不知道该文档是否可以保证,但由于兼容性的原因,现在这种行为现在已经被有效地修复了。这也是有道理的。

1

是的,当谓词满足一次就停止。这是通过展鹏反射代码:

[__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; 
    } 
3

你可以自己测试一下: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"); 
    } 
} 

是的,它:-)

也:一个同事建议沿东西线

if(!lotsOfItems.All(x => x.ID!= target.ID))

,因为这是应该停止一旦条件返回的第一次假,但我没把握,因此,如果任何人都可以在此提供一些线索,以及我们将不胜感激:>]

使用同样的道理

All()可以继续即使一个元素返回false :-)没有,甚至All()编程正确:-)

2

它确实不管是做什么,它必须做的最快捷的方式。

当上一个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 answerAny(predicate)相当。

Single是一个例外

更新:从这点以下上不再适用于.NET的核心,它改变的Single实施。

重要的是要注意,在linq-to对象的情况下,带有谓词的SingleSingleOrDefault的超负荷不会停止在发现的故障上。尽管对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)更安全。

相关问题