2010-12-06 23 views
44

Así que sé que Find() es solo un método , mientras que First() es una extensión para cualquier IEnumerable<T>. También sé que First() devolverá el primer elemento si no se pasa ningún parámetro, mientras que Find() lanzará una excepción. Por último, sé que First() lanzará una excepción si no se encuentra el elemento, mientras que Find() devolverá el valor predeterminado del tipo.C# Diferencia entre First() y Find()

Espero que aclare la confusión sobre lo que realmente estoy preguntando. Esta es una pregunta de ciencias de la computación y trata con estos métodos a nivel computacional. He llegado a comprender que las extensiones IEnumerable<T> no siempre funcionan como cabría esperar bajo el capó. Así que aquí está la Q, y me refiero desde un punto de vista "cercano al metal": ¿Cuál es la diferencia entre Find() y First()?

Aquí hay un código para proporcionar suposiciones básicas para operar bajo esta pregunta.

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

¿Hay alguna diferencia real entre computacional cómo First()Find() y descubrir sus valores en el código anterior?

Nota: Vamos a ignorar cosas como AsParallel() y AsQueryable() por el momento.

+0

Primero() creará un enumerador, Find() no lo hará. –

Respuesta

47

Aquí está el código para List<T>.Find (de 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); 
} 

Y aquí es 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(); 
} 

Así que ambos métodos funcionan más o menos de la misma manera: que iterar todos los artículos hasta que encuentran uno que coincide con el predicado. La única diferencia notable es que Find utiliza un bucle for porque ya conoce la cantidad de elementos, y First usa un bucle foreach porque no lo conoce.

+1

¿Qué es este reflector y cómo lo obtengo? – Squirrelsama

+4

Es un desensamblador .NET, puedes obtenerlo gratis en Redgate: http://www.red-gate.com/products/reflector/. La versión Pro no es gratuita, pero le permite recorrer el código del framework o ensamblajes de terceros. –

+0

Gracias. Entonces Foreach crea un enumerador, ¿verdad? ¿Tenemos un código para eso de tu Reflector? – Squirrelsama

1

Dado que List<> no está indexado de ninguna manera, tiene que pasar por todos los valores para encontrar un valor específico. Por lo tanto, no hace mucha diferencia en comparación con atravesar la lista a través de un enumerable (aparte de la creación de una instancia enumerable de objeto auxiliar).

Dicho esto, ten en cuenta que la función Find fue creado a principios de manera que el método First extensión (V2.0 V3.5 vs Marco), y dudo que hubieran implementado Find si la clase List<> había sido implementado al mismo tiempo que los métodos de extensión.

16

First arrojará una excepción cuando no encuentre nada, FirstOrDefault sin embargo, hace exactamente lo mismo que Find (aparte de cómo itera a través de los elementos).

0

¿Sería también cierto que usar Buscar en algo que es un enumerador en lugar de una Lista tendrá un costo de rendimiento potencial ya que un enumerador puede no tener que buscar toda la lista para satisfacer el predicado? Por el contrario, si ya tiene una lista, Find sería mejor.

+1

Buscar solo está definido en la Lista, por lo que no se pudo invocar directamente en I Enumerable. Si quisiste decir 'foo.ToList(). Find (x => ...)', entonces sí, en ese caso sería mejor usar 'foo.FirstOrDefault (x => ...)' ya que no lo haría No es necesario buscar toda la colección si coincide temprano. Si ya tiene una lista, Find es probablemente un poco más rápido (debido a 'for' vs' foreach') pero probablemente no sea suficiente para preocuparse en la mayoría de los casos. – ShawnFumo

1

BTW Buscar es bastante igual a FirstOrDefault() que a First(). Porque si el predicado de First() no está satisfecho con cualquier elemento de la lista, obtendrá una excepción. Aquí lo que devuelve un dotpeek, otro gran reemplazo del reflector libre con algunas de las características ReSharper

Aquí para Enumerable.First(...) y Enumerable.FirstOrDefault(...) extensión métodos:

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

y aquí es para la Lista <> .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); 
} 
+0

¿Quería obtener el código para 'FirstOrDefault' o' First'? Su método actual se llama 'First ' .. – Default

+0

ha corregido mi publicación. 'Find()' y 'FirstOrDefault()' devuelven 'default (T)' si no se encuentra nada. 'First()' sin embargo arroja una 'InvalidOperationException' –

+0

De acuerdo con la información de intellisense, * Find() * emitirá una excepción si no hay elementos coincidentes. Eso hace que funcione de manera mucho más parecida a * First() * que * FirstOrDefault() *, al contrario de su declaración en la respuesta. ¿He entendido mal el punto? –