2010-07-12 5 views
6

Recentemente sto sviluppando un software che analizza e visualizza informazioni XML da un sito web. Abbastanza semplice vero?Mi sembra di essere caduto in un enorme, massiccio problema con NullReferenceExceptions

Ricevo dei CARICHI di NullReferenceExceptions. Ad esempio, questo metodo:

private void SetUserFriends(List<Friend> list) 
{ 
    int x = 40; 
    int y = 3; 

    if (list != null) 
    { 
     foreach (Friend friend in list) 
     { 
      FriendControl control = new FriendControl(); 
      control.ID = friend.ID; 
      control.URL = friend.URL; 
      control.SetID(friend.ID); 
      control.SetName(friend.Name); 
      control.SetImage(friend.Photo); 

      control.Location = new Point(x, y); 
      panel2.Controls.Add(control); 

      y = y + control.Height + 4; 
     } 
    } 
} 

ho dovuto avvolgere una brutta come il peccato Se tutto il ciclo foreach reale al fine di evitare che un'eccezione.

Mi sembra di mettere gli cerotti su una gomma a terra invece di risolvere il problema. C'è un modo per affrontare questo problema? Forse un libro che dovrei leggere sui pattern di programmazione o cosa no?

Davvero, mi sono perso. Probabilmente sto facendo le domande sbagliate.

+8

Si dovrebbe guardare il codice che * chiama * 'SetUserFriends'. Se si assume che la lista degli amici non debba essere 'null' (che è un'ipotesi abbastanza giusta, direi), allora il bug è in qualunque cosa passi * in *' null'. Utilizzare il debugger per cercare lo stack di chiamate quando si ottiene l'eccezione. –

+1

Controllare meglio il motivo per cui si dispone di un riferimento Elenco nullo invece di un oggetto Elenco vuoto. – BenV

+3

Questa è una nota a margine, ma direi di accettare IEnumerable , in modo che il metodo non richieda al chiamante di utilizzare una determinata classe di raccolta. –

risposta

15

Sembra che non si sappia cosa fare se si ricevono parametri errati nei metodi. Non c'è niente di intrinsecamente sbagliato con quello che stai facendo ora, ma il modello più comune è quello di verificare i parametri nella testa del tuo metodo, un'eccezione se non sono quello che ci si aspetta:

if (list == null) 
{ 
    throw new ArgumentNullException(list); 
} 

Questo è uno schema di programmazione difensivo comune: verifica che i dati che ti vengono forniti superino i controlli di integrità di base.

Ora, se si sta chiamando questo metodo esplicitamente da soli e si sta trovando questo metodo che riceve un parametro list nullo quando non lo si aspetta, è il momento di esaminare la logica del metodo di chiamata. Io preferisco passare una lista vuota quando non ho elementi, al contrario di null, per evitare casi speciali come questo.

+0

+1 Se lo fai in modo coerente, imparerai con dolore come scrivere codice che eviti i null non intenzionali. –

+1

+1 per la programmazione difensiva –

4

Probabilmente sto andando ottenere downvoted dalla folla "no multipla uscita" ma io di solito gestire che con un semplice controllo proprio all'inizio del metodo:

if (list == null || list.Count == 0) return; 

questa specifica le condizioni di uscita e quindi non devi preoccuparti di più livelli di indentazione nel tuo metodo. Funziona solo se puoi permetterti di inghiottire il fatto che la tua lista sia nullo o vuota - cosa che può accadere in alcuni casi.

Ma concordo con codeka, in quanto è necessario esaminare il codice chiamante e capire se è possibile migliorarlo da lì.

+4

Questo approccio * potrebbe * funzionare per questo caso specifico, ma in generale porta a bug molto sottili. La maggior parte delle volte non è possibile inferire ragionevolmente ciò che il chiamante intendeva passando in 'null', quindi è più sicuro abortire con un'eccezione. –

+2

Dovrebbe essere documentato se un metodo accetta o meno, e (idealmente) cosa fa se questa pre-condizione non viene soddisfatta. Sono d'accordo con @Rex che i resi silenziosi possono spesso danneggiare il debugging. –

+0

Sono pienamente d'accordo con entrambi, ma ci sono casi in cui può essere utile –

0

Vorrei tornare in anticipo (o lanciare una InvalidArgumentException in anticipo) quando viene fornito un input non valido.

Ad esempio:

private void SetUserFriends(List<Friend> list) 
{ 
    if (list == null) 
     return; 

    /* Do stuff */ 
} 

In alternativa è possibile utilizzare il modello nullo generale coalescenza:

private void SetUserFriends(List<Friend> list) 
{ 
    list = list ?? new List<Friend>(); 

    /* Do Stuff */ 
} 
+1

La proposta di coalizione nulla è un po 'ridicola per questo scenario. –

+1

Il ritorno anticipato e il lancio sono * estremamente * diversi approcci. Quale raccomandi? –

+0

Uhm, se stai per lanciare un'eccezione, quella giusta sarebbe "ArgumentNullException", come ha mostrato l'esempio di Michael. –

1

Così come generare eccezioni ArgumentNullException c'è anche qualcosa chiamato il "Null Obejct Pattern", che si può utilizzare se si si desidera passare il valore di null, per indicare, ad esempio, che qualcosa non esiste, ma non si desidera dover verificare esplicitamente i valori null. Essenzialmente è una classe stub che implementa la stessa interfaccia, ma i suoi metodi di solito sono vuoti o restituiscono quel tanto che basta per renderli compatibili. http://en.wikipedia.org/wiki/Null_Object_pattern

Utile anche per tipi di valore che non possono facilmente esprimere la loro non esistenza, non potendo essere nulli.

+0

Interessante, ma non sono sicuro che si applichi qui, poiché la cosa più vicina al modello di oggetto nullo qui sarebbe mantenere una lista vuota in giro. L'altro problema, che ho sottolineato altrove, è che questo metodo dovrebbe probabilmente accettare un oggetto IEnumerable invece di un elenco. –

+0

Vero, ma ha detto che questo è solo un esempio dei suoi "CARICHI di NullReferenceExceptions". Potrebbe essere la soluzione giusta per alcuni dei suoi altri. Per questo, lancerei anche l'eccezione. – Spike

+0

La mia impressione è che non si tratta di dimenticare di controllare null e più di non avere idea del perché fosse nullo in primo luogo. Lo scopo del modello di oggetto nullo è di evitare i controlli nulli, ma non fornirà alcuna comprensione del problema più profondo qui. Eccezioni sarebbero, tuttavia, perché avrebbero frenare la propagazione dei valori nulli, rendendo più facile seguire la traccia dello stack in cui un nullo si intrufolava. –

2

Sembra che la programmazione difensiva e la convalida dei parametri siano ciò che state cercando.

Come altri hanno detto, semplice convalida dei parametri avrebbe funzionato per voi:

if (list == null) 
    throw new ArgumentNullException("list"); 

In alternativa, se siete stanchi di scrivere costantemente controlli in questo modo per ogni parametro, si potrebbe verificare uno dei tanti open source. Librerie di imposizione preliminare NET. Sono appassionato di CuttingEdge.Conditions.

In questo modo, è possibile utilizzare qualcosa di simile:

Condition.Requires(list, "list").IsNotNull(); 

Tuttavia, la creazione di una precondizione, come uno di quanto sopra sarà solo specificare che il metodo non accetta valori null. Il tuo problema persisterà ancora nel fatto che tu sono passando nulli nel metodo! Per risolvere il problema, dovrai esaminare i metodi che stanno chiamando e capire perché vengono inoltrati oggetti nulli.

+0

+1 per lo stato attivo sulla causa principale. –

+1

E +1 per referenziare CuttingEdge.Conditions ;-) – Steven

0

Stai davvero ponendo la domanda sbagliata. La domanda giusta è "null rappresenta un input non valido o un flag che significa X".

Fino a quando non vengono aggiunti tipi di riferimento non nulli alla lingua e si fanno strada varie API, è possibile scegliere di renderlo esplicito nel codice o lasciare che le eccezioni di riferimento null aiutino a trovare dove viene violata l'aspettativa e quindi a correggere dati/codice in un modo o nell'altro.

Problemi correlati