2016-11-28 57 views
3

作爲一個幼稚的提示,您經常會聽到使用IEnumerable.Any(),因爲那麼整個枚舉不一定需要遍歷。編譯器是否會優化與IEnumerable <T> .Count()的比較?

我只寫了一小段代碼,試圖查看Enumerable是否包含單個項目或多個項目。

if (reportInfo.OrebodyAndPits.SelectMany(ob => ob.Pits).Count() > 1) 
{ 
    ws.Cells[row, col++].Value = "Pits"; 
} 
else 
{ 
    ws.Cells[row, col++].Value = "Pit"; 
} 

這讓我不知道,會比較被編譯成一種形式,是足夠聰明,因爲它列舉過去的第一個項目儘快返回false?

如果不是,有沒有辦法寫一個linq擴展方法可以做到這一點?

(請注意,我不是在這一段代碼的性能影響非常感興趣。我主要好奇。)

+2

不,'.Count()'返回項目的數量,例如。 '123456789',然後檢查'> 1'條件。把'Skip(1).Any()'換成聰明的行爲。在某些情況下(這個*排除*).Net發現'IEnumerable '實際上是一個* array *'T []'或* list *'List '並且調用'Length'或'Count'而不是遍​​歷,但這就是我們所能預料的所有 –

回答

4

不,不會。您的代碼將計算序列中的所有項目。這是因爲LINQ語句沒有被編譯器優化,你寫的是你得到的。

的equivelent,序列是否包含超過100項檢查的更有效的方法是:

reportInfo.OrebodyAndPits.SelectMany(ob => ob.Pits).Skip(1).Any(); 

這將檢查,跳過第一個項目後,是否有任何物品離開。

3

如果你想知道如何工作,爲什麼不看源代碼?

這裏的Any()方法:https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/AnyAll.cs#L20

這裏是Count()方法:https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/Count.cs#L12

像你描述的編譯器不能做出優化。它要求計數並獲得一個數字,然後將該數字與條件語句中的數字進行比較。

然而,它嘗試做某種優化。從Count()方法中可以看出,它試圖查看IEnumerable是否已經支持Count屬性並使用它,因爲它比再次對所有元素進行計數要快。如果不可用,它必須遍歷整個事物並分別計數。

如果您想編寫一個LINQ方法(它只是IEnumerable<T>上的擴展方法),它確定IEnumerable中是否至少有兩個,那麼這應該很容易。例如:

例如

public static bool AtLeastTwo<TSource>(this IEnumerable<TSource> source) 
    { 
     if (source == null) 
     { 
      throw Error.ArgumentNull(nameof(source)); 
     } 

     using (IEnumerator<TSource> e = source.GetEnumerator()) 
     { 
      e.MoveNext(); // Move past the first one 
      return e.MoveNext(); // true if there is at least a second element. 
     } 
    } 
+0

跳過+任何更簡單並且不需要擴展方法。 – Wazner

+0

但這並不是被問到的:「如果沒有,是否有辦法寫一個linq擴展方法來做到這一點?」 –