Les détails exacts diffèrent un peu entre .NET Framework et .NET Core, mais cela dépend aussi un peu de ce que vous faites : si vous utilisez un fichier de type ICollection
ou ICollection<T>
(comme avec List<T>
), il existe un .Count
dont l'accès est peu coûteux, alors que d'autres types peuvent nécessiter une énumération.
TL;DR :
Utilice .Count > 0
si le bien existe, et sinon .Any()
.
Utilisation de .Count() > 0
es jamais la meilleure option, et dans certains cas, elle pourrait être beaucoup plus lente.
Cela s'applique à la fois à .NET Framework et à .NET Core.
Maintenant, nous pouvons plonger dans les détails
Listes et collections
Commençons par un cas très courant : l'utilisation de la fonction List<T>
(qui est aussi ICollection<T>
).
El .Count
est mis en œuvre comme :
private int _size;
public int Count {
get {
Contract.Ensures(Contract.Result<int>() >= 0);
return _size;
}
}
Ce que cela veut dire, c'est que _size
est maintenu par Add()
, Remove()
etc, et puisqu'il s'agit simplement d'accéder à un champ, cette opération est extrêmement bon marché - nous n'avons pas besoin d'itérer sur les valeurs.
ICollection
y ICollection<T>
les deux ont .Count
y le plus qui les mettent en œuvre sont susceptibles de le faire de manière similaire.
Autres IEnumerables
Tout autre IEnumerable
qui ne sont pas aussi ICollection
nécessitent une énumération de départ pour déterminer s'ils sont vides ou non. Le facteur clé affectant les performances est de savoir si nous finissons par énumérer un seul élément (idéal) ou la collection entière (relativement coûteux).
Si la collection provoque effectivement des entrées/sorties, par exemple en lisant une base de données ou un disque, les performances peuvent en souffrir.
Cadre .NET .Any()
Dans le .NET Framework (4.8), la fonction Any()
la mise en œuvre est :
public static bool Any<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
using (IEnumerator<TSource> e = source.GetEnumerator()) {
if (e.MoveNext()) return true;
}
return false;
}
Cela signifie que quoi qu'il arrive, il va obtenir un nouvel objet énumérateur et essayer d'itérer une fois. Ceci est plus coûteux que d'appeler la méthode List<T>.Count
mais au moins, il ne s'agit pas d'itérer la liste entière.
Cadre .NET .Count()
Dans le .NET Framework (4.8), la fonction Count()
est (fondamentalement) :
public static int Count<TSource>(this IEnumerable<TSource> source)
{
ICollection<TSource> collection = source as ICollection<TSource>;
if (collection != null)
{
return collection.Count;
}
int num = 0;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num = checked(num + 1);
}
return num;
}
}
Si disponible, ICollection.Count
est utilisé, mais sinon la collection est énumérée.
.NET Core .Any()
Le LINQ Any()
dans .NET Core est beaucoup plus intelligente. Vous pouvez voir le source complète ici mais les éléments pertinents pour cette discussion :
public static bool Any<TSource>(this IEnumerable<TSource> source)
{
//..snip..
if (source is ICollection<TSource> collectionoft)
{
return collectionoft.Count != 0;
}
//..snip..
using (IEnumerator<TSource> e = source.GetEnumerator())
{
return e.MoveNext();
}
}
Parce qu'un List<T>
es un ICollection<T>
ce qui appelle le Count
(et bien qu'il appelle une autre méthode, il n'y a pas d'allocations supplémentaires).
.NET Core .Count()
L'implémentation .NET Core ( source ) est fondamentalement le même que celui de .NET Framework (voir plus haut), et il utilisera donc la fonction ICollection.Count
si elle est disponible, et sinon, elle énumère la collection.
Résumé
Cadre .NET
.NET Core
-
.Count > 0
est la meilleure solution, si elle est disponible ( ICollection
)
-
.Any()
est bien, et fera soit ICollection.Count > 0
ou énumérer un seul élément
-
.Count() > 0
est mauvais car il provoque une énumération complète
0 votes
Il est préférable d'utiliser Any() sur les Enumérables et Count sur les Collections. Si quelqu'un pense qu'écrire '(somecollection.Count > 0)' sera source de confusion ou de problèmes de lisibilité, il vaut mieux l'écrire comme une méthode d'extension et la nommer Any(). Tout le monde sera alors satisfait. Du point de vue des performances et de la lisibilité. Ainsi, tout votre code sera cohérent et chaque développeur de votre projet ne devra pas s'inquiéter de choisir entre Count et Any.
2 votes
Vous avez vu Count() > 0 vs Any(), mais avez-vous vu Distinct().Count() > 1 vs Distinct().Skip(1).Any() ? Cette dernière est définitivement beaucoup plus rapide pour un grand nombre d'éléments où Count doit itérer sur l'ensemble pour obtenir un compte. Skip(1).Any() évite l'énumération complète. 100k itérations de la vérification d'un tableau de 1000 éléments avec des chaînes de 1 caractère qui s'exécute en environ 4000ms pour Count() > 1, s'exécute en seulement 20ms pour Skip(1).Any().