Подтвердить что ты не робот

С# Разница между First() и Find()

Итак, я знаю, что 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().

4b9b3361

Ответ 1

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

Было бы также верно, что использование "Найти" в том, что является перечислителем, а не в списке, будет иметь потенциальную стоимость исполнения, поскольку перечислителю может не потребоваться получить весь список для удовлетворения предиката? И наоборот, если у вас уже есть список, то Find будет лучше.

Ответ 5

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();
    }

и вот для List < > . Find:

/// <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);
}