2010-09-13 40 views
3

我想知道FirstOrDefault擴展方法是如何工作的?它遵循以下哪種算法?FirstOrDefault擴展方法如何工作?

用途:

var arr = new[] {1, 2, 3, 4, 5, 6, 7}; 
return arr.FirstOrDefault(x => x%2 == 0); 

算法1:

for(int i = 0; i < arr.Length; i++) 
{ 
    if(arr[i] % 2 == 0) 
    return arr[i]; 
} 
return 0; 

算法2:

var list = new List<int>(); 
for(int i = 0; i < arr.Length; i++) 
{ 
    if(arr[i] % 2 == 0) 
    list.Add(arr[i]); 
} 
return list.Count == 0 ? 0 : list[0]; 

是否FirstOrDefault算法是足夠聰明,選擇最佳的一個,或者遵循任何的這些算法之一?

+1

看看反射器。 – 2010-09-13 11:57:56

+1

概念上第一個雖然實際實現是不同的 – 2010-09-13 12:09:10

回答

8

我看了Reflector

public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source) 
{ 
    if (source == null) 
    { 
     throw Error.ArgumentNull("source"); 
    } 
    IList<TSource> list = source as IList<TSource>; 
    if (list != null) 
    { 
     if (list.Count > 0) 
     { 
      return list[0]; 
     } 
    } 
    else 
    { 
     using (IEnumerator<TSource> enumerator = source.GetEnumerator()) 
     { 
      if (enumerator.MoveNext()) 
      { 
       return enumerator.Current; 
      } 
     } 
    } 
    return default(TSource); 
} 

它試圖用List做到這一點,如果集合可以強制轉換爲IList的(並實現Count屬性)。否則它使用枚舉器。

編輯:謂詞的其他方法(我現在看到你在談論)不是最優化的,並依靠IEnumerable接口來執行foreach而不是IList。

public static TSource FirstOrDefault<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 local; 
     } 
    } 
    return default(TSource); 
} 
+0

由於我們正在討論選擇最佳算法......最近我注意到Linq的Any方法並不遵循與第一個和第一個計數相同的模式'方法;即它不檢查源是否實現了ICollection,以檢查ICollection.Count> 0。相反,它始終使用IEnumerator.MoveNext()來查看源是否爲空。我發現雖然性能比List 更好地進行ICollection類型檢查,但當source是Array時,性能會更差。在我看來,將Array投射到IList或ICollection是一個重大的性能損失。 – 2010-09-13 14:53:08

1

都不是,它使用一個枚舉僅讀取第一個值。當沒有第一個值時,它返回null(或者說,當前<T>的默認值)。