C # Разница между First () и Find () - PullRequest
50 голосов
/ 06 декабря 2010

Итак, я знаю, что Find() - это всего лишь List<T> метод, тогда как First() - это расширение для любого IEnumerable<T>. Я также знаю, что First() вернет первый элемент, если не передан ни один параметр, тогда как Find() выдаст исключение. Наконец, я знаю, что First() сгенерирует исключение, если элемент не найден, тогда как Find() вернет значение типа по умолчанию.

Надеюсь, это прояснит путаницу из-за того, что я на самом деле спрашиваю. Это вопрос информатики и касается этих методов на вычислительном уровне. Я понял, что расширения IEnumerable<T> не всегда работают так, как можно было бы ожидать под капотом. Итак, вот вопрос, и я имею в виду с точки зрения «близко к металлу»: в чем разница между 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().

Ответы [ 6 ]

53 голосов
/ 06 декабря 2010

Вот код для List<T>.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, потому что он не знает его.

19 голосов
/ 06 декабря 2010

First сгенерирует исключение, когда ничего не найдет, однако FirstOrDefault делает то же самое, что и Find (не считая того, как оно проходит по элементам).

5 голосов
/ 02 сентября 2013

BTW 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);
}
2 голосов
/ 02 марта 2019

1- Find() возвращает Null, если объект находится не в контексте, но First() выдаст исключение
2- Find() возвращает сущности, которые были добавлены в контекст, но еще не сохранены в базе данных

2 голосов
/ 06 декабря 2010

Поскольку List<> никоим образом не индексируется, необходимо найти все значения, чтобы найти конкретное значение.Поэтому это не имеет большого значения по сравнению с обходом списка через перечислимый (кроме создания экземпляра вспомогательного объекта перечислимого).

Тем не менее, имейте в виду, что функция Find быласоздан намного раньше, чем метод расширения First (Framework V2.0 против V3.5), и я сомневаюсь, что они реализовали бы Find, если бы класс List<> был реализован одновременно с методами расширения.

0 голосов
/ 01 февраля 2013

Верно ли и то, что использование Find для чего-то, что является перечислителем, а не List, будет иметь потенциальную потерю производительности, поскольку перечислителю, возможно, не придется извлекать весь список для удовлетворения предиката?И наоборот, если у вас уже есть список, то поиск будет лучше.

...