Quindi mi Do.Something(...)
spesso in questa situazione … dove Do.Something(...)
restituisce una raccolta nulla, in questo modo:
int[] returnArray = Do.Something(...);
Quindi, provo ad usare questa collezione in questo modo:
foreach (int i in returnArray) { // do some more stuff }
Sono solo curioso, perché un ciclo foreach non può funzionare su una raccolta nulla? Mi sembra logico che 0 iterazioni vengano eseguite con una raccolta null … invece lancia una NullReferenceException
. Qualcuno sa perché questo potrebbe essere?
Questo è fastidioso perché sto lavorando con API che non sono chiare esattamente su ciò che restituiscono, quindi if (someCollection != null)
con if (someCollection != null)
ovunque …
Modifica: Grazie a tutti per aver spiegato che foreach
utilizza GetEnumerator
e se non è disponibile alcun enumeratore, il foreach fallirebbe. Suppongo che sto chiedendo perché la lingua / runtime non può o non vuole fare un controllo nullo prima di afferrare l’enumeratore. Mi sembra che il comportamento sarebbe ancora ben definito.
Bene, la risposta breve è “perché è così che i progettisti del compilatore lo hanno progettato”. Realisticamente, però, il tuo object collection è nullo, quindi non c’è modo per il compilatore di far scorrere l’enumeratore attraverso la collezione.
Se hai davvero bisogno di fare qualcosa di simile, prova l’operatore null coalescing:
int[] array = null; foreach (int i in array ?? Enumerable.Empty()) { System.Console.WriteLine(string.Format("{0}", i)); }
Un ciclo foreach
chiama il metodo GetEnumerator
.
Se la raccolta è null
, questa chiamata al metodo genera una NullReferenceException
.
È una ctriggers pratica restituire una raccolta null
; i tuoi metodi dovrebbero invece restituire una raccolta vuota.
C’è una grande differenza tra una raccolta vuota e un riferimento null a una collezione.
Quando si utilizza foreach
, internamente, si chiama il metodo GetEnumerator () di IEnumerable. Quando il riferimento è nullo, questo solleverà questa eccezione.
Tuttavia, è perfettamente valido avere un object IEnumerable
o IEnumerable
vuoto. In questo caso, foreach non “eseguirà iterazioni” su nulla (poiché la raccolta è vuota), ma non genererà nemmeno un lancio, poiché si tratta di uno scenario perfettamente valido.
Modificare:
Personalmente, se hai bisogno di ovviare a questo, ti consigliamo un metodo di estensione:
public static IEnumerable AsNotNull (this IEnumerable original) { return original ?? Enumerable.Empty (); }
Puoi quindi solo chiamare:
foreach (int i in returnArray.AsNotNull()) { // do some more stuff }
Un altro metodo di estensione per aggirare questo problema:
public static void ForEach(this IEnumerable items, Action action) { if(items == null) return; foreach (var item in items) action(item); }
Consumare in diversi modi:
(1) con un metodo che accetta T
:
returnArray.ForEach(Console.WriteLine);
(2) con un’espressione:
returnArray.ForEach(i => UpdateStatus(string.Format("{0}% complete", i)));
(3) con un metodo anonimo multilinea
int toCompare = 10; returnArray.ForEach(i => { var thisInt = i; var next = i++; if(next > 10) Console.WriteLine("Match: {0}", i); });
Basta scrivere un metodo di estensione per aiutarti:
public static class Extensions { public static void ForEachWithNull(this IEnumerable source, Action action) { if(source == null) { return; } foreach(var item in source) { action(item); } } }
Perché una raccolta nulla non è la stessa cosa di una collezione vuota. Una collezione vuota è un object di raccolta senza elementi; una raccolta nullo è un object inesistente.
Ecco qualcosa da provare: dichiara due raccolte di qualsiasi tipo. Inizializzarne uno normalmente in modo che sia vuoto e assegnare all’altro il valore null
. Quindi prova ad aggiungere un object ad entrambe le collezioni e vedere cosa succede.
Si sta rispondendo a lungo, ma ho cercato di farlo nel modo seguente per evitare l’eccezione del puntatore nullo e potrebbe essere utile per qualcuno che usa l’operatore di controllo null C #.
//fragments is a list which can be null fragments?.ForEach((obj) => { //do something with obj });
È colpa di Do.Something()
. La migliore pratica qui sarebbe quella di restituire una matrice di dimensione 0 (che è ansible) invece di null.
Perché dietro le quinte il foreach
acquisisce un enumeratore, equivalente a questo:
using (IEnumerator enumerator = returnArray.getEnumerator()) { while (enumerator.MoveNext()) { int i = enumerator.Current; // do some more stuff } }
Penso che la spiegazione del perché venga lanciata un’eccezione è molto chiara con le risposte fornite qui. Vorrei solo completare il mio modo di lavorare di solito con queste collezioni. Perché, alcune volte, uso la raccolta più di una volta e devo verificare se null ogni volta. Per evitare ciò, faccio quanto segue:
var returnArray = DoSomething() ?? Enumerable.Empty(); foreach (int i in returnArray) { // do some more stuff }
In questo modo possiamo usare la collezione tanto quanto vogliamo senza temere l’eccezione e non polarizziamo il codice con dichiarazioni condizionali eccessive.
Utilizzando l’operatore di controllo nullo ?.
è anche un ottimo approccio. Ma, in caso di matrici (come nell’esempio nella domanda), dovrebbe essere trasformato in Elenco prima:
int[] returnArray = DoSomething(); returnArray?.ToList().ForEach((i) => { // do some more stuff });
SPListItem item; DataRow dr = datatable.NewRow(); dr["ID"] = (!Object.Equals(item["ID"], null)) ? item["ID"].ToString() : string.Empty;