Comment vérifier si IEnumerable est nul ou vide?

155

J'adore la string.IsNullOrEmptyméthode. J'adorerais avoir quelque chose qui permettrait la même fonctionnalité pour IEnumerable. Y en a-t-il un? Peut-être une classe d'aide à la collecte? La raison pour laquelle je demande est que dans les ifdéclarations, le code semble encombré si le motif l'est (mylist != null && mylist.Any()). Ce serait beaucoup plus propre à avoir Foo.IsAny(myList).

Ce message ne donne pas cette réponse: IEnumerable est vide? .

Schultz9999
la source
1
@msarchet: Je vous donnerais probablement la réponse si ce n'était pas le commentaire :)
Schultz9999
pour moi, cela semble être une sorte de problème XY. au lieu de demander "comment puis-je vérifier la valeur null exactement partout sans que cela soit si gênant", vous devriez demander "comment puis-je améliorer ma conception pour ne pas avoir à vérifier la valeur nulle partout?"
sara le
@nawfal, la question à laquelle vous avez lié n'inclut pas spécifiquement les vérifications nulles, donc je ne la considérerais pas comme un doublon
Mygeen

Réponses:

188

Bien sûr, vous pouvez écrire cela:

public static class Utils {
    public static bool IsAny<T>(this IEnumerable<T> data) {
        return data != null && data.Any();
    }
}

cependant, veillez à ce que toutes les séquences ne soient pas répétables; en général, je préfère ne les promener qu'une seule fois, au cas où.

Marc Gravell
la source
12
Est-ce un bon modèle? Je voudrais laisser tomber thislà - je considère les méthodes d'extension qui sont supposées être appelées nullcomme un signe de conception laide.
Mormegil
28
@Mormegil Pourquoi? Les méthodes d'extension donnent enfin à C # une certaine capacité à travailler avec des valeurs nulles, ce que d'autres langages (comme Ruby) tiennent complètement pour acquis.
Matt Greer
5
Pourquoi est-ce nécessairement mauvais? Comme dans ce cas, il est parfois très pratique car il permet de traiter les choses de manière plus homogène et avec moins de cas particuliers.
M. Putty
5
@Mormegil meh - Je ne peux pas m'exciter à ce sujet. Tant que l'intention est claire, etc.
Marc Gravell
6
@Miryafa .Any()est une méthode d'extension qui fonctionne sur IEnumerable<T>(ou IQueryable<T>, bien que ce soit un scénario différent). Cela consomme la séquence , au moins partiellement (bien que cela signifie toujours qu'elle est consommée) - il se peut qu'elle ne doive lire qu'un seul élément (surtout s'il n'y a pas de prédicat). En tant que tel, puisque les séquences ( IEnumerable<T>) n'ont pas besoin d'être répétables, c'est peut-être le cas . Any()sans prédicat équivaut essentiellement à foreach(var x in sequence) { return true; } return false;- bien qu'il utilise GetEnumerator()etc au lieu de la syntaxe du compilateur
Marc Gravell
120
public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable) {
    return enumerable == null || !enumerable.Any();
}
Matt Greer
la source
8
enfin, pas tout à fait, l'OP a demandé IEnumerable, pas IEnumerable <T> ;-)
yoyo
8
Oui, IEnumerablen'a pas d' Any()extension.
Blaise
23

Voici une version modifiée de la réponse utile de @Matt Greer qui inclut une classe wrapper statique afin que vous puissiez simplement copier-coller ceci dans un nouveau fichier source, ne dépend pas de Linq, et ajoute une IEnumerable<T>surcharge générique , pour éviter la boxing des types de valeur cela se produirait avec la version non générique. [EDIT: Notez que l'utilisation de IEnumerable<T>n'empêche pas la mise en boîte de l'énumérateur, le typage canard ne peut pas empêcher cela, mais au moins les éléments d'une collection à valeur typée ne seront pas chacun encadrés.]

using System.Collections;
using System.Collections.Generic;

public static class IsNullOrEmptyExtension
{
    public static bool IsNullOrEmpty(this IEnumerable source)
    {
        if (source != null)
        {
            foreach (object obj in source)
            {
                return false;
            }
        }
        return true;
    }

    public static bool IsNullOrEmpty<T>(this IEnumerable<T> source)
    {
        if (source != null)
        {
            foreach (T obj in source)
            {
                return false;
            }
        }
        return true;
    }
}
yo-yo
la source
15

Une autre façon serait d'obtenir l'énumérateur et d'appeler la méthode MoveNext () pour voir s'il y a des éléments:

if (mylist != null && mylist.GetEnumerator().MoveNext())
{
    // The list is not null or empty
}

Cela fonctionne pour IEnumerable ainsi que IEnumerable <T>.

Darren
la source
4
Devriez-vous appeler Dispose sur cet enquêteur? Si la collection prend en charge le multithreading? Oui. stackoverflow.com/questions/13459447/…
TamusJRoyce
2
@TamusJRoyce Notez que votre déclaration n'est vraie que pour IEnumerable<T>, car non générique IEnumerablene l'implémente pas IDisposable.
Ian Kemp
9

La façon dont je le fais, en tirant parti de certaines fonctionnalités modernes de C #:

Option 1)

public static class Utils {
    public static bool IsNullOrEmpty<T>(this IEnumerable<T> list) {
        return !(list?.Any() ?? false);
    }
}

Option 2)

public static class Utils {
    public static bool IsNullOrEmpty<T>(this IEnumerable<T> list) {
        return !(list?.Any()).GetValueOrDefault();
    }
}

Et d'ailleurs, ne jamais utiliser Count == 0ou Count() == 0juste pour vérifier si une collection est vide. Utilisez toujours Linq's.Any()

Ronald Rey
la source
2
Count == 0 est très bien .... Peut-être plus rapide que Any ()? Cependant, vous avez raison de dire que Count () == 0 est mauvais. Pour ceux qui se demandent, Count () parcourt toute votre collection, donc si c'est énorme, cela pourrait ajouter une tonne de frais généraux!
Anthony Nichols
Count () n'itère l'énumération que si elle ne peut pas être convertie en ICollection. En d'autres termes, lorsque vous appelez cette méthode, s'il existe déjà une propriété Count sur l'objet, elle retournera simplement cela et les performances devraient être identiques. Découvrez l'implémentation ici: referencesource.microsoft.com/#System.Core/System/Linq/...
Ronald Rey
Si vous travaillez avec un IEnumerable, utiliser Count () pour tester le vide est certainement une mauvaise idée car l'implémentation Linq va itérer sur toute la collection, tandis que Any déplacera simplement l'itérateur une fois. Gardez à l'esprit que vous ne pouvez pas utiliser la propriété Count dans ce cas, car elle ne fait pas partie de l'interface IEnumerable. C'est pourquoi il est toujours préférable d'utiliser simplement Any () pour tester le vide dans tous les scénarios, à mon avis.
Ronald Rey
Bel exemple de la façon dont l'opérateur de négation non lisible !peut être, spécialement dans la deuxième option;)
Fabio
6

Cela peut aider

public static bool IsAny<T>(this IEnumerable<T> enumerable)
{
    return enumerable?.Any() == true;
}

public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
{
    return enumerable?.Any() != true;
}
Hossein Narimani Rad
la source
5

À partir de C # 6, vous pouvez utiliser la propagation nulle :myList?.Any() == true

Si vous trouvez toujours cela trop encombrant ou préférez une bonne méthode d'extension, je recommanderais les réponses de Matt Greer et Marc Gravell, mais avec un peu de fonctionnalités étendues pour être complet.

Leurs réponses fournissent les mêmes fonctionnalités de base, mais chacune dans une autre perspective. La réponse de Matt utilise la string.IsNullOrEmpty-mentalité, tandis que la réponse de Marc prend celle de Linq.Any() route pour faire le travail.

Je suis personnellement enclin à utiliser la .Any()route, mais je voudrais ajouter la fonctionnalité de vérification de l'état de l' autre surcharge de la méthode :

    public static bool AnyNotNull<T>(this IEnumerable<T> source, Func<T, bool> predicate = null)
    {
        if (source == null) return false;
        return predicate == null
            ? source.Any()
            : source.Any(predicate);
    }

Ainsi, vous pouvez toujours faire des choses comme: myList.AnyNotNull(item=>item.AnswerToLife == 42);comme vous le pourriez avec le.Any() mais avec la vérification nulle ajoutée

Notez qu'avec la méthode C # 6: myList?.Any()retourne a bool?plutôt que a bool, qui est l'effet réel de la propagation de null

Thomas Mulder
la source
1
Le problème avec la collection? .Any () est que ce n'est pas transitif. Lorsqu'elle est nulle, collection? .Any () == true est false, mais collection? .Any () == false est également false. De plus
,!
4
if (collection?.Any() == true){
    // if collection contains more than one item
}
if (collection?.Any() != true){
    // if collection is null
    // if collection does not contain any item
}
Scholtz
la source
2

Voici le code de la réponse de Marc Gravell , avec un exemple d'utilisation.

using System;
using System.Collections.Generic;
using System.Linq;

public static class Utils
{
    public static bool IsAny<T>(this IEnumerable<T> data)
    {
        return data != null && data.Any();
    }
}

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<string> items;
        //items = null;
        //items = new String[0];
        items = new String[] { "foo", "bar", "baz" };

        /*** Example Starts Here ***/
        if (items.IsAny())
        {
            foreach (var item in items)
            {
                Console.WriteLine(item);
            }
        }
        else
        {
            Console.WriteLine("No items.");
        }
    }
}

Comme il le dit, toutes les séquences ne sont pas répétables, de sorte que le code peut parfois causer des problèmes, car IsAny()commence à parcourir la séquence. Je soupçonne que la réponse de Robert Harvey voulait dire que vous n'avez souvent pas besoin de vérifier null et de vider. Souvent, vous pouvez simplement vérifier la valeur null, puis utiliserforeach .

Pour éviter de démarrer la séquence deux fois et en profiter foreach, j'ai juste écrit un code comme celui-ci:

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<string> items;
        //items = null;
        //items = new String[0];
        items = new String[] { "foo", "bar", "baz" };

        /*** Example Starts Here ***/
        bool isEmpty = true;
        if (items != null)
        {
            foreach (var item in items)
            {
                isEmpty = false;
                Console.WriteLine(item);
            }
        }
        if (isEmpty)
        {
            Console.WriteLine("No items.");
        }
    }
}

Je suppose que la méthode d'extension vous évite quelques lignes de frappe, mais ce code me semble plus clair. Je soupçonne que certains développeurs ne se rendraient pas immédiatement compte que IsAny(items)cela commencerait réellement à parcourir la séquence. (Bien sûr, si vous utilisez beaucoup de séquences, vous apprenez rapidement à réfléchir à leurs étapes.)

Don Kirkby
la source
Si vous appelez IsAny sur un null, il lancera une exception
Ace Trajkov
3
Avez-vous essayé, @Ace? Il semble que cela lèverait une exception, mais les méthodes d'extension peuvent être appelées sur des instances nulles .
Don Kirkby le
2

j'utilise Bool IsCollectionNullOrEmpty = !(Collection?.Any()??false); . J'espère que cela t'aides.

Panne:

Collection?.Any()retournera nullsi Collection est nul, etfalse si Collection est vide.

Collection?.Any()??falsenous donnera falsesi Collection est vide, et falsesi Collection estnull .

Un complément de cela nous donnera IsEmptyOrNull.

Sabyasachi Mukherjee
la source
2

La réponse de Jon Skeet ( https://stackoverflow.com/a/28904021/8207463 ) a une bonne approche en utilisant la méthode d'extension - Any () pour NULL et EMPTY. MAIS il valide le propriétaire des questions en cas de NOT NULL. Modifiez donc soigneusement l'approche de Jon pour valider AS NULL en:

If (yourList?.Any() != true) 
{
     ..your code...
}

NE PAS utiliser (ne validera pas AS NULL):

If (yourList?.Any() == false) 
{
     ..your code...
}

Vous pouvez également en cas de validation AS NOT NULL (PAS testé comme exemple mais sans erreur du compilateur) faire quelque chose comme utiliser le prédicat:

If (yourList?.Any(p => p.anyItem == null) == true) 
{
     ..your code...
}

https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,8788153112b7ffd0

Pour quelle version .NET vous pouvez l'utiliser, veuillez vérifier:

https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.any?view=netframework-4.8#moniker-applies-to

Juste juste
la source
1

J'ai eu le même problème et je le résolve comme:

    public bool HasMember(IEnumerable<TEntity> Dataset)
    {
        return Dataset != null && Dataset.Any(c=>c!=null);
    }

"c => c! = null" ignorera toutes les entités nulles.

Hosein Djadidi
la source
1

J'ai construit cela à partir du réponse de @Matt Greer

Il a parfaitement répondu à la question du PO.

Je voulais quelque chose comme ça tout en conservant les capacités d'origine de Any tout en vérifiant la valeur null. Je poste ceci au cas où quelqu'un d'autre aurait besoin de quelque chose de similaire.

Plus précisément, je voulais toujours pouvoir passer un prédicat.

public static class Utilities
{
    /// <summary>
    /// Determines whether a sequence has a value and contains any elements.
    /// </summary>
    /// <typeparam name="TSource">The type of the elements of source.</typeparam>
    /// <param name="source">The <see cref="System.Collections.Generic.IEnumerable"/> to check for emptiness.</param>
    /// <returns>true if the source sequence is not null and contains any elements; otherwise, false.</returns>
    public static bool AnyNotNull<TSource>(this IEnumerable<TSource> source)
    {
        return source?.Any() == true;
    }

    /// <summary>
    /// Determines whether a sequence has a value and any element of a sequence satisfies a condition.
    /// </summary>
    /// <typeparam name="TSource">The type of the elements of source.</typeparam>
    /// <param name="source">An <see cref="System.Collections.Generic.IEnumerable"/> whose elements to apply the predicate to.</param>
    /// <param name="predicate">A function to test each element for a condition.</param>
    /// <returns>true if the source sequence is not null and any elements in the source sequence pass the test in the specified predicate; otherwise, false.</returns>
    public static bool AnyNotNull<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        return source?.Any(predicate) == true;
    }
}

La dénomination de la méthode d'extension pourrait probablement être meilleure.

kb4000
la source
0

L'autre meilleure solution comme ci-dessous pour vérifier vide ou non?

for(var item in listEnumerable)
{
 var count=item.Length;
  if(count>0)
  {
         // not empty or null
   }
  else
  {
       // empty
  }
}
Shakeer Hussain
la source
1
Cela ne fonctionnera pas si listEnumerableest nul, ce qui est la question à portée de main
Timotei
0

J'utilise celui-ci:

    public static bool IsNotEmpty(this ICollection elements)
    {
        return elements != null && elements.Count > 0;
    }

Ejem:

List<string> Things = null;
if (Things.IsNotEmpty())
{
    //replaces ->  if (Things != null && Things.Count > 0) 
}
Jhollman
la source
0

Étant donné que certaines ressources sont épuisées après une lecture, j'ai pensé pourquoi ne pas combiner les contrôles et les lectures, au lieu du contrôle séparé traditionnel, puis lire.

Tout d'abord, nous en avons un pour l'extension inline check-for-null plus simple:

public static System.Collections.Generic.IEnumerable<T> ThrowOnNull<T>(this System.Collections.Generic.IEnumerable<T> source, string paramName = null) => source ?? throw new System.ArgumentNullException(paramName ?? nameof(source));

var first = source.ThrowOnNull().First();

Ensuite, nous avons l'extension en ligne un peu plus impliquée (enfin, du moins la façon dont je l'ai écrite): check-for-null-and-empty inline:

public static System.Collections.Generic.IEnumerable<T> ThrowOnNullOrEmpty<T>(this System.Collections.Generic.IEnumerable<T> source, string paramName = null)
{
  using (var e = source.ThrowOnNull(paramName).GetEnumerator())
  {
    if (!e.MoveNext())
    {
      throw new System.ArgumentException(@"The sequence is empty.", paramName ?? nameof(source));
    }

    do
    {
      yield return e.Current;
    }
    while (e.MoveNext());
  }
}

var first = source.ThrowOnNullOrEmpty().First();

Vous pouvez bien sûr toujours appeler les deux sans continuer la chaîne d'appels. Aussi, j'ai inclus le paramName, de sorte que l'appelant puisse inclure un autre nom pour l'erreur si ce n'est pas "source" en cours de vérification, par exemple "nameof (target)".

Rob
la source
0
 public static bool AnyNotNull<TSource>(this IEnumerable<TSource> source)
    {
        return source != null && source.Any();
    }

ma propre méthode d'extension pour vérifier Non nul et Any

NobDev
la source
0

Sans assistants personnalisés, je recommande l'un ?.Any() ?? falseou l' autre ou ?.Any() == truequi sont relativement concis et n'ont besoin de spécifier la séquence qu'une seule fois.


Lorsque je veux traiter une collection manquante comme une collection vide, j'utilise la méthode d'extension suivante:

public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T> sequence)
{
    return sequence ?? Enumerable.Empty<T>();
}

Cette fonction peut être combinée avec toutes les méthodes LINQ et foreach, pas seulement .Any(), c'est pourquoi je la préfère aux fonctions d'assistance plus spécialisées que les gens proposent ici.

CodesInChaos
la source
0

j'utilise

    list.Where (r=>r.value == value).DefaultIfEmpty().First()

Le résultat sera nul si aucune correspondance, sinon retourne l'un des objets

Si vous vouliez la liste, je pense que quitter First () ou appeler ToList () fournira la liste ou null.

Ron Chibnik
la source
0

Jetez un œil à cette bibliothèque Open Source : Nzr.ToolBox

public static bool IsEmpty(this System.Collections.IEnumerable enumerable)
Mario Santos
la source
-1

il suffit d'ajouter using System.Linqet de voir la magie se produire lorsque vous essayez d'accéder aux méthodes disponibles dans le IEnumerable. L'ajout de cela vous donnera accès à une méthode nommée Count()aussi simple que cela. n'oubliez pas de vérifier null valueavant d'appeler count():)

Mohit
la source
-1

J'ai utilisé simple si pour le vérifier

regarde ma solution

foreach (Pet pet in v.Pets)
{
    if (pet == null)
    {
        Console.WriteLine(" No pet");// enumerator is empty
        break;
    }
    Console.WriteLine("  {0}", pet.Name);
}
Basheer AL-MOMANI
la source