Come verificare se IEnumerable è null o vuoto?


155

Adoro il string.IsNullOrEmptymetodo. Mi piacerebbe avere qualcosa che consentirebbe la stessa funzionalità per IEnumerable. C'è questo? Forse un po 'di aiuto nella raccolta? Il motivo che sto chiedendo è che nelle ifdichiarazioni il codice sembra ingombra se lo scalpiccio è (mylist != null && mylist.Any()). Sarebbe molto più pulito da avere Foo.IsAny(myList).

Questo post non dà questa risposta: IEnumerable è vuoto? .


1
@msarchet: probabilmente ti darei la risposta se questo non fosse il commento :)
Schultz9999,

per me questo sembra una specie di problema XY. invece di chiedere "come posso verificare la presenza di null esattamente dappertutto senza che sia così fastidioso" dovresti chiedere "come posso migliorare il mio design in modo da non dover controllare null ovunque?"
Sara,

@nawfal, la domanda a cui ti sei collegato non include specificamente i controlli null, quindi non lo considero un duplicato
Mygeen

Risposte:


188

Sicuro che potresti scrivere che:

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

tuttavia, fai attenzione che non tutte le sequenze siano ripetibili; generalmente preferisco camminare solo una volta, per ogni evenienza.


12
È un buon modello? Vorrei lasciar perdere this- considero i metodi di estensione che si presume siano chiamati nullcome un segno di brutto design.
Mormegil,

28
@Mormegil Perché? i metodi di estensione danno finalmente a C # alcune capacità di lavorare con null, che altri linguaggi (come Ruby) danno completamente per scontato.
Matt Greer,

5
Perché questo è necessariamente negativo? Come in questo caso, a volte è molto utile poiché consente di trattare le cose in modo più omogeneo e con meno casi speciali.
Putty,

5
@Mormegil meh - Non posso eccitarmi per questo. Finché l'intento è chiaro, ecc.
Marc Gravell

6
@Miryafa .Any()è un metodo di estensione che opera su IEnumerable<T>(o IQueryable<T>, sebbene sia uno scenario diverso). In questo modo si consuma la sequenza , almeno in parte (anche se ciò significa che viene consumata) - potrebbe essere necessario leggere solo un elemento (soprattutto se non esiste un predicato). Pertanto, poiché le sequenze ( IEnumerable<T>) non devono essere ripetibili, potrebbe essere così . Any()senza predicato equivale essenzialmente a foreach(var x in sequence) { return true; } return false;- sebbene utilizzi GetEnumerator()etc invece della sintassi del compilatore
Marc Gravell

120
public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable) {
    return enumerable == null || !enumerable.Any();
}

8
beh, non proprio, l'OP ha chiesto IEnumerable, non IEnumerable <T> ;-)
yoyo

8
Sì, IEnumerablenon ha Any()estensione.
Blaise,

23

Ecco una versione modificata dell'utile risposta di @Matt Greer che include una classe wrapper statica in modo da poter semplicemente copiare e incollare questo in un nuovo file di origine, non dipende da Linq e aggiunge un IEnumerable<T>sovraccarico generico , per evitare l'inscatolamento dei tipi di valore ciò si verificherebbe con la versione non generica. [EDIT: Nota che l'uso di IEnumerable<T>non impedisce la boxe dell'enumeratore, la tipizzazione anatra non può impedirlo, ma almeno gli elementi in una raccolta tipizzata con valore non saranno ciascuno inscatolati.]

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

15

Un altro modo sarebbe quello di ottenere l'Enumeratore e chiamare il metodo MoveNext () per vedere se ci sono elementi:

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

Questo funziona per IEnumerable e IEnumerable <T>.


4
Dovresti chiamare dispose su questo enumeratore? Se la raccolta è a conoscenza del multithreading? Sì. stackoverflow.com/questions/13459447/...
TamusJRoyce

2
@TamusJRoyce Nota che la tua affermazione è vera solo IEnumerable<T>perché non generica IEnumerablenon implementa IDisposable.
Ian Kemp,

9

Il modo in cui lo faccio, sfruttando alcune moderne funzionalità di C #:

Opzione 1)

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

Opzione 2)

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

E a proposito, non usare mai Count == 0o Count() == 0semplicemente per verificare se una raccolta è vuota. Usa sempre Linq's.Any()


2
Count == 0 va bene .... Forse più veloce di Any ()? Tuttavia hai ragione sul fatto che Count () == 0 sia errato. Per quelli che si stanno chiedendo Count () scorre tutta la tua collezione, quindi se è enorme potrebbe aggiungere un sacco di spese generali!
Anthony Nichols,

Count () itera l'enumerazione solo se non può essere trasmesso a ICollection. In altre parole, quando chiami questo metodo, se sull'oggetto è già presente una proprietà Count, la restituirà e le prestazioni dovrebbero essere identiche. Dai un'occhiata all'implementazione qui: riferimentiource.microsoft.com/#System.Core/System/Linq/…
Ronald Rey

Se stai lavorando con un IEnumerable, usare Count () per testare la svuotamento è sicuramente una cattiva idea poiché l'implementazione di Linq eseguirà l'iterazione sull'intera raccolta, mentre Any sposta l'iteratore una volta sola. Tieni presente che in questo caso non puoi utilizzare la proprietà Count poiché non fa parte dell'interfaccia IEnumerable. Ecco perché è sempre una buona idea usare Any () per verificare la vuoto in tutti gli scenari, secondo me.
Ronald Rey,

Un bell'esempio di come !può essere un operatore di negazione non leggibile , specialmente nella seconda opzione;)
Fabio,

6

Questo può aiutare

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

5

A partire da C # 6 puoi usare la propagazione nulla :myList?.Any() == true

Se lo trovi ancora troppo sporco o preferisci un buon vecchio metodo di estensione, consiglierei le risposte di Matt Greer e Marc Gravell, ma con un po 'di funzionalità estesa per completezza.

Le loro risposte forniscono le stesse funzionalità di base, ma ognuna da un'altra prospettiva. La risposta di Matt usa la string.IsNullOrEmptymentalità, mentre la risposta di Marc prende la .Any()strada di Linq per portare a termine il lavoro.

Sono personalmente propenso a utilizzare la .Any()strada, ma vorrei aggiungere la funzionalità di controllo delle condizioni dall'altro sovraccarico del metodo :

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

Quindi puoi ancora fare cose del genere: myList.AnyNotNull(item=>item.AnswerToLife == 42);come potresti fare con il normale.Any() ma con il controllo null aggiunto

Si noti che con il modo C # 6: myList?.Any()restituisce a bool?anziché a bool, che è l'effetto effettivo della propagazione di null


1
Il problema con la raccolta? Qualunque () non è transitivo. Quando null, collection? .Any () == true è false, ma collection? .Any () == false è anche false. Inoltre
,!

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
}

2

Ecco il codice dalla risposta di Marc Gravell , insieme a un esempio di utilizzo.

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.");
        }
    }
}

Come dice, non tutte le sequenze sono ripetibili, quindi il codice a volte può causare problemi, perché IsAny()inizia a scorrere la sequenza. Ho il sospetto che la risposta di Robert Harvey significasse che spesso non è necessario controllare null e svuotare. Spesso, puoi semplicemente verificare la presenza di null e quindi utilizzare foreach.

Per evitare di avviare la sequenza due volte e trarne vantaggio foreach, ho appena scritto un codice come questo:

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.");
        }
    }
}

Immagino che il metodo di estensione ti salvi un paio di righe di battitura, ma questo codice mi sembra più chiaro. Ho il sospetto che alcuni sviluppatori non si IsAny(items)renderanno immediatamente conto che inizierà effettivamente a passare attraverso la sequenza. (Naturalmente se stai usando molte sequenze, impari rapidamente a pensare a ciò che le attraversa.)


Se si chiama IsAny su null, si genererà un'eccezione
Ace Trajkov

3
Hai provato, @Ace? Sembra che genererebbe un'eccezione, ma i metodi di estensione possono essere chiamati su istanze null .
Don Kirkby,

2

Io uso Bool IsCollectionNullOrEmpty = !(Collection?.Any()??false);. Spero che questo ti aiuti.

Abbattersi:

Collection?.Any()restituirà nullse Collection è null e falsese Collection è vuota.

Collection?.Any()??falseci darà falsese Collection è vuota e falsese Collection lo è null.

Il complemento di questo ci darà IsEmptyOrNull.


2

La risposta di Jon Skeet ( https://stackoverflow.com/a/28904021/8207463 ) ha un buon approccio usando il metodo di estensione - Any () per NULL ed EMPTY. MA sta convalidando il proprietario delle domande nel caso NOT NULL. Quindi modifica con attenzione l'approccio di Jon per convalidare AS NULL in:

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

NON utilizzare (non convaliderà come NULL):

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

Puoi anche validare AS NOT NULL (NON testato solo come esempio ma senza errore del compilatore) fare qualcosa come usare predicato:

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

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

Per quale versione .NET è possibile utilizzarla, consultare:

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


1

Ho avuto lo stesso problema e lo risolvo come:

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

"c => c! = null" ignorerà tutte le entità null.


1

Ho creato questo dalla risposta di @Matt Greer

Ha risposto perfettamente alla domanda del PO.

Volevo qualcosa del genere mantenendo le funzionalità originali di Any e verificando anche null. Sto pubblicando questo nel caso in cui qualcun altro abbia bisogno di qualcosa di simile.

In particolare, volevo essere ancora in grado di passare un predicato.

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 denominazione del metodo di estensione potrebbe probabilmente essere migliore.


0

L'altra migliore soluzione come sotto per controllare vuoto o no?

for(var item in listEnumerable)
{
 var count=item.Length;
  if(count>0)
  {
         // not empty or null
   }
  else
  {
       // empty
  }
}

1
Ciò non funzionerà se listEnumerableè nullo, che è la domanda a portata di mano
Timotei,

0

Io uso questo:

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

0

Dato che alcune risorse sono esaurite dopo una lettura, ho pensato perché non combinare i controlli e le letture, anziché il tradizionale controllo separato, quindi leggere.

Innanzitutto ne abbiamo uno per l'estensione inline check-for-null più semplice:

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

Quindi abbiamo un po 'più coinvolto (beh, almeno il modo in cui l'ho scritto) check-for-null-and-empty inline extension:

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

Ovviamente puoi sempre chiamare entrambi senza continuare la catena di chiamate. Inoltre, ho incluso il paramName, in modo che il chiamante possa includere un nome alternativo per l'errore se non viene verificato "origine", ad esempio "nomeof (destinazione)".


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

il mio metodo di estensione per selezionare Not null and Any


0

Senza gli helper personalizzati consiglio uno ?.Any() ?? falseo ?.Any() == trueche sono relativamente concisi e devono solo specificare una volta la sequenza.


Quando voglio trattare una collezione mancante come una vuota, utilizzo il seguente metodo di estensione:

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

Questa funzione può essere combinata con tutti i metodi LINQ e foreach, non solo .Any(), è per questo che la preferisco alle funzioni di supporto più specializzate che la gente propone qui.


0

Io uso

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

Il risultato sarà nullo se nessuna corrispondenza, altrimenti restituisce uno degli oggetti

Se volevi la lista, credo che lasciare First () o chiamare ToList () fornirà la lista o null.


0

Dai un'occhiata a questa libreria opensource: Nzr.ToolBox

public static bool IsEmpty(this System.Collections.IEnumerable enumerable)

-1

basta aggiungere using System.Linqe vedere la magia che accade quando si tenta di accedere ai metodi disponibili in IEnumerable. L'aggiunta di questo ti darà accesso a un metodo chiamato Count()così semplice. ricordati di controllare null valueprima di chiamare count():)


-1

Ho usato semplice se per verificarlo

controlla la mia soluzione

foreach (Pet pet in v.Pets)
{
    if (pet == null)
    {
        Console.WriteLine(" No pet");// enumerator is empty
        break;
    }
    Console.WriteLine("  {0}", pet.Name);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.