【问题标题】:C# Difference between First() and Find()C# First() 和 Find() 的区别
【发布时间】:2010-12-06 17:12:43
【问题描述】:

所以我知道Find() 只是List<T> 方法,而First() 是任何IEnumerable<T> 的扩展。我也知道First() 会在没有参数传递的情况下返回第一个元素,而Find() 会抛出异常。最后,我知道First()会在找不到元素时抛出异常,而Find()会返回类型的默认值。

我希望这可以消除我对实际问题的困惑。这是一个计算机科学问题,在计算级别处理这些方法。我开始明白IEnumerable<T> 扩展并不总是像人们期望的那样运行。所以这里是 Q,我的意思是从“接近金属”的角度来看:Find() 和 First() 之间有什么区别?

这里有一些代码可以为这个问题提供基本假设。

var l = new List<int> { 1, 2, 3, 4, 5 };
var x = l.First(i => i == 3);
var y = l.Find(i => i == 3);

First() 和 Find() 如何在上面的代码中发现它们的值之间是否存在任何实际的计算差异?

注意:让我们暂时忽略 AsParallel() 和 AsQueryable() 之类的内容。

【问题讨论】:

  • First() 将创建一个枚举器,Find() 不会。

标签: c# linq list c#-4.0


【解决方案1】:

这是List&lt;T&gt;.Find 的代码(来自 Reflector):

public T Find(Predicate<T> match)
{
    if (match == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        if (match(this._items[i]))
        {
            return this._items[i];
        }
    }
    return default(T);
}

这里是Enumerable.First:

public static TSource First<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;
        }
    }
    throw Error.NoMatch();
}

所以这两种方法的工作方式大致相同:它们迭代所有项目,直到找到与谓词匹配的项目。唯一明显的区别是Find 使用for 循环,因为它已经知道元素的数量,而First 使用foreach 循环,因为它不知道。

【讨论】:

  • 这个反射器是什么,我如何获得它?
  • 这是一个 .NET 反汇编程序,您可以从 Redgate 免费获得它:red-gate.com/products/reflector。专业版不是免费的,但它允许您逐步执行框架或第三方程序集的代码。
  • @pcnThird,Reflector 很久以前就不再免费了,但是有几个免费的替代品。查看 JustDecompile (Telerik)、dotPeek (Jetbrains) 或 ILSpy (开源)
  • 接受的答案是错误的。 “唯一明显的区别”是 First() 如果没有找到匹配项,则抛出异常,而 Find() 返回默认值(在大多数情况下为 null)。这意味着如果您使用 Find 并获取 null,您不知道它是指“未找到匹配项”还是“找到序列中的空元素”。
  • @synek317 原发帖者提到他们已经知道异常引发的差异,并且更担心实际查找代码本身是如何工作的。在这种情况下,我认为这个答案是正确的。
【解决方案2】:

First 在没有发现任何内容时会抛出异常,FirstOrDefault 但与Find 完全相同(除了它如何遍历元素)。

【讨论】:

    【解决方案3】:

    顺便说一句,Find 等于FirstOrDefault() 而不是First()。因为如果First() 的谓词对任何列表元素都不满意,您将获得异常。 这里返回一个dotpeek,另一个很棒的免费反射器替代品,带有一些 ReSharper 功能

    这里是Enumerable.First(...) 和Enumerable.FirstOrDefault(...) 扩展方法:

        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 element in source) { 
                if (predicate(element)) return element;
            } 
            return default(TSource); 
        }
    
    
        public static TSource First<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 element in source) {
                if (predicate(element)) return element; 
            }
            throw Error.NoMatch();
        }
    

    这里是列表。查找:

    /// <summary>
    /// Searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire <see cref="T:System.Collections.Generic.List`1"/>.
    /// </summary>
    /// 
    /// <returns>
    /// The first element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type <paramref name="T"/>.
    /// </returns>
    /// <param name="match">The <see cref="T:System.Predicate`1"/> delegate that defines the conditions of the element to search for.</param><exception cref="T:System.ArgumentNullException"><paramref name="match"/> is null.</exception>
    [__DynamicallyInvokable]
    public T Find(Predicate<T> match)
    {
      if (match == null)
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
      for (int index = 0; index < this._size; ++index)
      {
        if (match(this._items[index]))
          return this._items[index];
      }
      return default (T);
    }
    

    【讨论】:

    • 您是要获取FirstOrDefault 或First 的代码吗?您当前的方法名为First&lt;T&gt;..
    • 已更正我的帖子。如果没有找到,Find() 和 FirstOrDefault() 将返回 default(T)。 First() 但是会抛出 InvalidOperationException
    • 根据intellisense的信息,如果没有匹配的元素,Find()会抛出异常。这使得它与 First() 的工作方式比 FirstOrDefault() 更相似,这与您在回复中的陈述相反。我误解了重点吗?
    • Find() 如果 "match" 参数为空,则抛出 ArgumentNullException,如果没有匹配的元素则不会
    【解决方案4】:

    1- 如果实体不在上下文中,Find() 返回Null,但First() 将抛出异常
    2- Find() 返回已添加到上下文但尚未保存到数据库的实体

    【讨论】:

      【解决方案5】:

      由于List&lt;&gt; 没有以任何方式索引,它必须遍历所有值才能找到特定值。因此,与通过可枚举遍历列表相比(除了创建可枚举的辅助对象实例之外),它并没有太大的区别。

      也就是说,请记住 Find 函数的创建时间早于 First 扩展方法(Framework V2.0 与 V3.5),我怀疑他们是否会实现 Find 如果List&lt;&gt; 类与扩展方法同时实现。

      【讨论】:

        猜你喜欢
        • 2016-01-06
        • 1970-01-01
        • 2021-02-20
        • 1970-01-01
        • 2022-01-21
        • 2015-10-03
        • 2017-03-19
        • 1970-01-01
        相关资源
        最近更新 更多