2015-05-15 36 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)更安全。

相關問題