【问题标题】:How FirstOrDefault extension method works?FirstOrDefault 扩展方法如何工作?
【发布时间】:2010-09-13 11:53:47
【问题描述】:

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

用途:

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

算法一:

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 算法是否足够聪明,可以选择最佳算法,还是严格遵循这些算法中的任何一种?

【问题讨论】:

  • 概念上是第一个,但实际实现不同

标签: c# .net extension-methods ienumerable


【解决方案1】:

我查看了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);
}

如果集合可以转换为 IList(并实现 Count 属性),它会尝试使用 List 执行此操作。否则它使用枚举器。

编辑:另一种带有谓词的方法(我现在看到你在谈论)没有优化,它依赖于 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);
}

【讨论】:

  • 既然我们在谈论选择最佳算法... 最近我注意到 Linq Any 方法与 FirstCount 方法不遵循相同的模式;即它不检查源是否实现 ICollection 以检查 ICollection.Count > 0。相反,它总是使用 IEnumerator.MoveNext() 来查看源是否为空。我发现虽然使用 List 进行 ICollection 类型检查的性能更好,但当源是数组时性能要差得多。在我看来,将 Array 转换为 IList 或 ICollection 会显着降低性能。
【解决方案2】:

First/FirstOrDefault 选择序列中的第一个元素,没什么聪明的。

【讨论】:

    【解决方案3】:

    也不是,它使用枚举器只读取第一个值。当没有第一个值时,它返回 null(或者更确切地说,当前&lt;T&gt; 的默认值)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-24
      • 2012-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多