2011-12-20 6 views
49

Conosco generalmente l'elenco vuoto è più preferibile di NULL. Ma restituirò NULL, principalmente per due ragioniC# ha IsNullOrEmpty per List/IEnumerable?

  1. Devo controllare e gestire i valori nulli esplicitamente, evitando bug e attacchi.
  2. È facile eseguire l'operazione ?? in seguito per ottenere un valore di ritorno.

Per le stringhe, abbiamo IsNullOrEmpty. C'è qualcosa da C# stesso facendo la stessa cosa per List o IEnumerable?

+6

No, ma l'aggiunta di un metodo di estensione è banale. –

+0

possibile duplicato di [Come verificare se IEnumerable è nullo o vuoto?] (Http://stackoverflow.com/questions/5047349/how-to-check-if-ienumerable-is-null-or-empty) – octothorpentine

risposta

54

nulla cotta nel quadro, ma è un metodo di estensione piuttosto semplice.

See here

/// <summary> 
    /// Determines whether the collection is null or contains no elements. 
    /// </summary> 
    /// <typeparam name="T">The IEnumerable type.</typeparam> 
    /// <param name="enumerable">The enumerable, which may be null or empty.</param> 
    /// <returns> 
    ///  <c>true</c> if the IEnumerable is null or empty; otherwise, <c>false</c>. 
    /// </returns> 
    public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable) 
    { 
     if (enumerable == null) 
     { 
      return true; 
     } 
     /* If this is a list, use the Count property for efficiency. 
     * The Count property is O(1) while IEnumerable.Count() is O(N). */ 
     var collection = enumerable as ICollection<T>; 
     if (collection != null) 
     { 
      return collection.Count < 1; 
     } 
     return !enumerable.Any(); 
    } 

Daniel Vaughan prende il passaggio aggiuntivo di colata a ICollection (ove possibile) per motivi di prestazioni. Qualcosa che non avrei pensato di fare.

+2

Si deve fare attenzione però che la chiamata a enumerable.Any() 'può perdere un elemento di una enumerazione non ripristinabile. Puoi avvolgerlo in qualcosa che tenga traccia di quel primo elemento (e gestisca bene nettamente naturalmente o neghiamo l'intero problema dell'OP), ma in alcuni casi può essere più comodo fondersi con un'enumerazione vuota e usare solo il risultato . –

+0

Uh, sono stupido o l'ultima riga di quella funzione dovrebbe essere 'return! Enumerable.Any();'? Lo modifico –

+1

Si dovrebbe anche provare a trasmettere a 'ICollection' non generico, poiché un' IEnumerable 'potrebbe essere qualcosa come un'istanza di' Lista ', che non implementa' ICollection 'ma implementa' ICollection' non generico? – supercat

20

Non c'è niente di costruito nel

Si tratta di un semplice metodo di estensione però:.

public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable) 
{ 
    if(enumerable == null) 
    return true; 

    return !enumerable.Any(); 
} 
+3

È necessario un "non" sulla collezione.Qualsiasi() chiamata. –

+0

@ChuckBlumreich - Grazie per la correzione. – Oded

+0

Anche perché la chiami collezione? è meglio chiamarlo enumerabile. –

9
var nullOrEmpty = list == null || !list.Any(); 
1

Se avete bisogno di essere in grado di recuperare tutti gli elementi nel caso in cui non sia vuoto, poi alcune delle risposte qui non funzionerà, perché la chiamata a Any() su un enumerabile non riavvolgibile "dimenticherà" un elemento.

Si potrebbe adottare un approccio diverso e girare i null in vuoti:

bool didSomething = false; 
foreach(var element in someEnumeration ?? Enumerable.Empty<MyType>()) 
{ 
    //some sensible thing to do on element... 
    didSomething = true; 
} 
if(!didSomething) 
{ 
    //handle the fact that it was null or empty (without caring which). 
} 

Allo stesso modo (someEnumeration ?? Enumerable.Empty<MyType>()).ToList() ecc può essere utilizzato.

+0

Potresti fornirmi un esempio di una raccolta non riavvolgibile in .NET? Intendo solo il nome di una classe esistente nel framework, non chiedendoti di darmi un esempio di implementazione di uno. – AaronLS

+0

@AaronLS il risultato della maggior parte delle query di Linq. –

+0

Quale sarebbe il tipo concreto in quel caso? Sono a conoscenza di IQueryable che non è realmente una lista finché non provi ToList/foreach a quel punto si è materializzato. Immagino ci sia un tipo di calcestruzzo intermedio per IQueryable che è un lettore di dati forward solo sotto il cofano, quindi quello che dici ha un senso, non riesco a immaginare dove potresti effettivamente incontrare questo problema. Se si 'foreach (var item in someQueryable)' genererebbe il proprio enumeratore e quindi 'someQueryable.Any()' sarebbe una query separata e non influenzerebbe l'attuale foreach enumerator AFAIK. – AaronLS

1

Come tutti gli altri hanno detto, nel framework non è stato creato nulla, ma se si utilizza Castle, Castle.Core.Internal ce l'ha.

using Castle.Core.Internal; 

namespace PhoneNumbers 
{ 
    public class PhoneNumberService : IPhoneNumberService 
    { 
     public void ConsolidateNumbers(Account accountRequest) 
     { 
      if (accountRequest.Addresses.IsNullOrEmpty()) // Addresses is List<T> 
      { 
       return; 
      } 
      ... 
0

ho modificato il suggerimento da Matthew Vines per evitare il "Possibile conteggio multiplo di IEnumerable" - problema. (si veda anche il commento da Jon Hanna)

public static bool IsNullOrEmpty(this IEnumerable items) 
    => items == null 
    || (items as ICollection)?.Count == 0 
    || !items.GetEnumerator().MoveNext(); 

... e la prova di unità:

[Test] 
public void TestEnumerableEx() 
{ 
    List<int> list = null; 
    Assert.IsTrue(list.IsNullOrEmpty()); 

    list = new List<int>(); 
    Assert.IsTrue(list.IsNullOrEmpty()); 

    list.AddRange(new []{1, 2, 3}); 
    Assert.IsFalse(list.IsNullOrEmpty()); 

    var enumerator = list.GetEnumerator(); 
    for(var i = 1; i <= list.Count; i++) 
    { 
     Assert.IsFalse(list.IsNullOrEmpty()); 
     Assert.IsTrue(enumerator.MoveNext()); 
     Assert.AreEqual(i, enumerator.Current); 
    } 

    Assert.IsFalse(list.IsNullOrEmpty()); 
    Assert.IsFalse(enumerator.MoveNext()); 
} 
0
var nullOrEmpty = !(list?.Count > 0); 
+0

Le risposte al solo codice non sono buone risposte, prova ad aggiungere alcune righe che spiegano quale fosse il problema e come il codice lo corregge – MikeT

+0

Questo non verrà compilato se l'elenco è IEnumerable. –

16

tardo aggiornamento: dal C# 6.0, l'operatore nullo propagazione possono essere utilizzati per esprimere concisa simili:

if (enumerable?.Any() ?? false) 

Nota 1:?? false è necessario, per i seguenti motivi (riepilogo/preventivo da this post):

?. operatore restituirà null se un membro di bambino è null. Ma [...] se cerchiamo di ottenere un non Nullable membro, come il metodo Any(), che restituisce bool [...] il compilatore "avvolgere" un valore di ritorno in Nullable<>. Ad esempio, Object?.Any() sarà darci bool? (che è Nullable<bool>), non bool. [...] Dal momento che non può essere implicitamente colato a bool questa espressione non può essere utilizzato nel if

Nota 2: come bonus, la dichiarazione è anche "thread-safe" (citazione dalla risposta di this question):

in un contesto multithread, se [enumerable] è accessibile da un altro filo (sia perché è un campo che è accessibile o perché è chiuso sopra in un lambda che è posta Xposed ad un altro thread) allora il valore potrebbe essere diverso ogni volta che viene calcolata [ieprior nullo-controllo]

+0

@ GrimR3 Sì, hai ragione: quello è più stretto alla domanda. L'ho scritto così per essere in linea con la citazione/referece. –

1

Mettendo insieme le risposte precedenti in un metodo semplice estensione per C# 6.0 +:

public static bool IsNullOrEmpty<T>(this IEnumerable<T> me) => !me?.Any() ?? true;