Je sais donc que Find()
n'est qu'une méthode List<T>
, Tandis que First()
est une extension pour tout IEnumerable<T>
. Je sais également que First()
renverra le premier élément si aucun paramètre n'est passé, tandis que Find()
lèvera une exception. Enfin, je sais que First()
lèvera une exception si l'élément n'est pas trouvé, tandis que Find()
renverra la valeur par défaut du type.
J'espère que cela dissipe la confusion sur ce que je demande réellement. Il s'agit d'une question informatique et traite de ces méthodes au niveau informatique. J'en suis venu à comprendre que les extensions IEnumerable<T>
Ne fonctionnent pas toujours comme on pourrait s'y attendre sous le capot. Voici donc le Q, et je veux dire d'un point de vue "proche du métal": Quelle est la différence entre Find()
et First()
?
Voici un code pour fournir des hypothèses de base pour fonctionner sous 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);
Existe-t-il une réelle différence de calcul entre la façon dont First()
et Find()
découvrent leurs valeurs dans le code ci-dessus?
Remarque: Ignorons pour l'instant des choses comme AsParallel()
et AsQueryable()
.
Voici le code pour List<T>.Find
(du réflecteur):
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'ils en trouvent un qui correspond au prédicat. La seule différence notable est que Find
utilise une boucle for
car il connaît déjà le nombre d'éléments, et First
utilise une boucle foreach car il ne le sait pas.
First
lèvera une exception quand il ne trouvera rien, FirstOrDefault
fait cependant exactement la même chose que Find
(à part comment il itère à travers les éléments).
BTW Find est plutôt égal à FirstOrDefault()
qu'à First()
. Parce que si le prédicat de First()
n'est satisfait d'aucun élément de liste, vous obtiendrez une exception. Voici ce qui renvoie un dotpeek , un autre grand remplacement gratuit du réflecteur avec certaines des fonctionnalités de ReSharper
Ici pour les méthodes d'extension Enumerable.First(...)
et 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();
}
et voici pour la liste <>.
/// <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);
}
Puisque List<>
n'est pas indexé en aucune façon, il doit parcourir toutes les valeurs pour trouver une valeur spécifique. Par conséquent, cela ne fait pas beaucoup de différence par rapport à la traversée de la liste via un énumérateur (à part la création d'une instance d'objet auxiliaire énumérable).
Cela dit, gardez à l'esprit que la fonction Find
a été créée bien avant la méthode d'extension First
(Framework V2.0 vs. V3.5), et je doute qu'ils auraient implémenté Find
si le List<>
classe avait été implémentée en même temps que les méthodes d'extension.
1- Find()
renvoie Null
si l'entité n'est pas dans le contexte mais First()
lèvera une exception
2- Find()
renvoie les entités qui ont été ajoutées au contexte mais qui n'ont pas encore été enregistrées dans la base de données