70 votes

C# Différence entre First() et Find()

Donc je sais que Find() est seulement un List<T> alors que First() est une extension pour tout IEnumerable<T> . Je sais aussi que First() retournera le premier élément si aucun paramètre n'est passé, alors que Find() entraînera une exception. Enfin, je sais que First() lèvera une exception si l'élément n'est pas trouvé, alors que Find() retournera la valeur par défaut du type.

J'espère que cela dissipe toute confusion sur ce que je demande réellement. Il s'agit d'une question d'informatique qui traite de ces méthodes au niveau du calcul. J'ai compris que IEnumerable<T> Les extensions ne fonctionnent pas toujours comme on pourrait s'y attendre sous le capot. Voici donc la question, et je veux dire d'un point de vue "proche du métal" : Quelle est la différence entre Find() y First() ?

Voici un code qui fournit des hypothèses de base pour répondre à cette question.

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

Y a-t-il une réelle différence de calcul entre la façon dont First() y Find() découvrir leurs valeurs dans le code ci-dessus ?

Note : Ignorons les choses comme AsParallel() y AsQueryable() pour le moment.

0 votes

First() va créer un énumérateur, Find() ne le fera pas.

71voto

Thomas Levesque Points 141081

Voici le code pour List<T>.Find (du 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);
}

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

Les deux méthodes fonctionnent donc à peu près de la même manière : elles itèrent tous les éléments jusqu'à ce qu'elles en trouvent un qui corresponde au prédicat. La seule différence notable est que Find utilise un for car il connaît déjà le nombre d'éléments, et First utilise une boucle foreach parce qu'il ne la connaît pas.

3 votes

Qu'est-ce que ce réflecteur et comment puis-je l'obtenir ?

6 votes

C'est un désassembleur .NET, vous pouvez l'obtenir gratuitement de Redgate : red-gate.com/produits/réflecteur . La version Pro n'est pas gratuite, mais elle vous permet de parcourir le code des assemblages de frameworks ou de tiers.

0 votes

Merci. Donc le foreach crée un énumérateur, n'est-ce pas ? Avons-nous du code pour cela dans votre Reflector ?

32voto

Doggett Points 1920

First lèvera une exception s'il ne trouve rien, FirstOrDefault fait cependant exactement la même chose que Find (à part la façon dont il itère à travers les éléments).

6voto

python_kaa Points 317

BTW Find est plutôt égal à FirstOrDefault() que de First() . Car si le prédicat de First() n'est satisfait d'aucun élément de la liste, vous obtiendrez une exception. Ici, ce qui renvoie un dotpeek , un autre grand remplacement gratuit de réflecteur avec certaines des caractéristiques de ReSharper

Ici pour Enumerable.First(...) y Enumerable.FirstOrDefault(...) les méthodes d'extension :

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

et voici pour 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);
}

0 votes

Vouliez-vous obtenir le code pour FirstOrDefault o First ? Votre méthode actuelle est nommée First<T> ..

0 votes

J'ai corrigé mon message. Find() y FirstOrDefault() sont de retour default(T) si rien n'est trouvé. First() Cependant, un InvalidOperationException

0 votes

Selon les informations d'intellisense, Trouver() lèvera une exception s'il n'y a pas d'éléments correspondants. Cela rend son fonctionnement beaucoup plus similaire à Premier() que FirstOrDefault() contrairement à ce que vous avez déclaré dans votre réponse. Ai-je mal compris le point ?

2voto

Lucero Points 38928

Depuis List<> n'est pas indexé de quelque manière que ce soit, il doit parcourir toutes les valeurs pour trouver une valeur spécifique. Par conséquent, cela ne fait pas une grande différence par rapport à la traversée de la liste via un énumérable (à part la création d'une instance d'objet d'aide énumérable).

Cela dit, n'oubliez pas que le Find a été créée bien plus tôt que la fonction First (Framework V2.0 vs. V3.5), et je doute qu'ils aient mis en place la méthode d'extension Find si le List<> avait été implémentée en même temps que les méthodes d'extension.

0voto

pkr2000 Points 95

Est-il également vrai que l'utilisation de Find sur quelque chose qui est un énumérateur plutôt qu'une liste aura un coût potentiel de performance puisqu'un énumérateur n'a pas besoin de récupérer la liste entière pour satisfaire le prédicat ? Inversement, si vous disposez déjà d'une liste, Find serait plus efficace.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X