2012-04-12 11 views
5

Recentemente ho avuto un bug di codifica in cui, in determinate condizioni, non veniva inizializzata una variabile e ricevevo un NullReferenceException. Ciò ha richiesto un po 'di tempo per eseguire il debug poiché dovevo trovare i bit di dati che generano questo per ricreare l'errore in quanto l'eccezione non fornisce il nome della variabile.Un modo migliore per gestire NullReferenceExceptions in C#

Ovviamente potrei controllare ogni variabile prima dell'uso e lanciare un'eccezione informativa ma c'è un modo migliore (leggi meno codice) per farlo? Un altro pensiero che avevo era la spedizione con i file pdb in modo che le informazioni di errore conterrebbero la riga di codice che ha causato l'errore. Come fanno gli altri a evitare/gestire questo problema?

Grazie

+1

Sempre "Asserire" liberamente per valori nulli. – leppie

+0

@leppie - questo è un sacco di affermazione! Sì, potevo farlo ma speravo in una soluzione più elegante per tenere conto di situazioni come me che mi dimentico di farlo :) –

+1

Protip: tieni i metodi piccoli (diciamo al massimo 10-20 linee), quindi non hai davvero bisogno dei numeri di riga . – leppie

risposta

11

In primo luogo: non fare troppo in una singola istruzione. Se hai un numero enorme di operazioni di dereferenziamento in una riga, sarà molto più difficile trovare il colpevole. Il Law of Demeter aiuta anche con questo - se hai qualcosa come order.SalesClerk.Manager.Address.Street.Length allora hai un sacco di opzioni da guadare quando ottieni un'eccezione. (Sono non dogmatica circa la Legge di Demetra, ma tutto con moderazione ...)

In secondo luogo: preferisco fusione rispetto all'uso di as, a meno che sia valida per l'oggetto sia un tipo diverso, che normalmente comporta un controllo nulla immediatamente dopo. Così qui:

// What if foo is actually a Control, but we expect it to be String? 
string text = foo as string; 
// Several lines later 
int length = text.Length; // Bang! 

Qui ci saremmo un NullReferenceException e, infine, risalire al text essere nulla - ma poi non si sa se è perché foo era nullo, o perché era un tipo di inaspettato. Se dovesse davvero, davvero essere un string, poi gettato invece:

string text = (string) foo; 

Ora sarete in grado di capire la differenza tra i due scenari.

In terzo luogo: come altri hanno già detto, convalidare i dati, in genere argomenti per API pubbliche e potenzialmente interne. Lo faccio in un numero sufficiente di posti in Noda Time che ho una classe di utilità per aiutarmi a decodificare il controllo. Così, per esempio (da Period):

internal LocalInstant AddTo(LocalInstant localInstant, 
          CalendarSystem calendar, int scalar) 
{ 
    Preconditions.CheckNotNull(calendar, "calendar"); 
    ... 
} 

Si dovrebbe documento ciò che può e non può essere nullo, anche.

+0

sì, quelli sono buoni consigli. Devo essere spedito con i file pdb in modo che venga data la linea? Attualmente il mio codice di produzione fornisce il nome del metodo ma non la linea che aggiunge un grado di difficoltà. –

+0

Insieme a 'non fare troppo su ogni riga' puoi aggiungere 'non fare troppo in ogni metodo'. Questo aiuta a minimizzare il problema. – Servy

+0

@directedbit: Dipende dalla tua situazione ... per il codice lato server, assolutamente. Se stai spedendo il codice lato client, * potrebbe * essere più sensibile o disordinato ... –

2

Mi dispiace dire che eseguirò sempre un controllo per verificare che qualsiasi oggetto che sto usando in un particolare metodo non sia nullo.

E 'semplice come

if(this.SubObject == null) 
{ 
    throw new Exception("Could not perform METHOD - SubObject is null."); 
} 
else 
{ 
... 
} 

Altrimenti non riesco a pensare a un modo di essere approfondita. Non avrebbe molto senso per me non fare questi controlli comunque; Sento che è solo una buona pratica.

+1

Spero che tu non butti solo Eccezione ... –

+0

Non sempre. Solitamente estendo l'eccezione. Questo è solo un esempio. – DigitalJedi805

+2

In questo caso si dovrebbe usare InvalidOperationException se lo stato stesso è valido - e ArgumentNullException per impedirti di entrare in quello stato se non lo è. –

2

Prima di tutto è necessario sempre convalidare i dati immessi. Se null non è consentito, lanciare un ArgumentNullException.

Ora, so come può essere doloroso, in modo da poter esaminare gli strumenti di riscrittura degli assemblaggi che lo fanno per te.L'idea è che si avrebbe una sorta di attributo che avrebbe segnato questi argomenti che non possono essere null:

public void Method([NotNull] string name) { ... 

E il masterizzatore sarebbe riempire gli spazi vuoti ...

O una semplice estensione metodo potrebbe rendere più facile

name.CheckNotNull(); 
+0

questo è un buon consiglio. Sfortunatamente questo era tutto me stesso. Avevo posizionato male una parentesi nella funzione in modo da utilizzare una variabile al di fuori dell'ambito dell'inizializzazione. –

3

in molti casi è quasi impossibile da pianificare e conto per ogni tipo di eccezione che potrebbe accadere in qualsiasi punto del flusso di esecuzione dell'applicazione. La codifica difensiva è efficace solo fino a un certo punto. Il trucco consiste nell'avere un solido stack di diagnostica incorporato nell'applicazione in grado di fornire informazioni significative sugli errori non gestiti e sugli arresti anomali. Avere un buon gestore di primo livello (ultimo fosso) a livello di dominio app ti aiuterà molto.

Sì, la spedizione dei PDB (anche con una versione di rilascio) è un buon modo per ottenere una traccia di stack completa in grado di individuare la posizione esatta e le cause degli errori. Ma qualunque sia l'approccio diagnostico che scegli, deve essere inserito nel progetto dell'applicazione per cominciare (idealmente). L'aggiornamento di un'app esistente può essere noioso e richiede tempo e denaro.

+0

grazie, c'è un modo che consiglio di cuocerlo. Attualmente ho il semplice approccio di loggare quanti più dettagli possibili per qualsiasi eccezione non gestita. –

+0

Il fatto è che dipende dal particolare metodo/posizione ...a volte vuoi informazioni specifiche su ciò che è successo perché potresti aspettarti determinati tipi di errori. In altri, come ho detto, potresti avere semplicemente una situazione inaspettata. La prima cosa che puoi fare è agganciare 'AppDomain.UnhandledException' (o' Application_Error' in global.asax per le applicazioni web), e quindi iniziare a riadattare i singoli metodi, se necessario. – kprobst

2

Se siete solo in cerca di un modo più compatto al codice contro avere riferimenti nulli, non trascurare l'operatore null coalescenza ??MSDN

Ovviamente, dipende da cosa si sta facendo, ma può essere usato per evitare dichiarazioni extra se

Problemi correlati