2010-09-13 132 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>的默认值)。