Come si fa a lanciare una ArgumentNullException?

26

Diciamo che ho un metodo:

public void DoSomething(ISomeInterface someObject)
{
    if(someObject == null) throw new ArgumentNullException("someObject");
    someObject.DoThisOrThat();
}

Sono stato addestrato a credere che il lancio di ArgumentNullException sia "corretto" ma un errore "Riferimento oggetto non impostato su un'istanza di un oggetto" significa che ho un bug.

Perché?

So che se memorizzo nella cache il riferimento a someObject e lo utilizzi in un secondo momento, allora è meglio controllare la nullità quando viene passato e fallire presto. Tuttavia, se sto effettuando il dereferimento sulla riga successiva, perché dovremmo fare il controllo? Porrà un'eccezione in un modo o nell'altro.

Modifica :

Mi è appena venuto in mente ... la paura del nulla dereferenziato proviene da un linguaggio come il C ++ che non controlla per te (cioè cerca solo di eseguire qualche metodo su posizione di memoria zero + offset del metodo)?

    
posta Scott Whitlock 22.11.2011 - 18:37
fonte

7 risposte

33

Suppongo che se si sta deferendo immediatamente la variabile, si potrebbe discutere in entrambi i modi, ma preferirei comunque l'ArgumentNullException.
È molto più esplicito su cosa sta succedendo. L'eccezione contiene il nome della variabile che era null, mentre una NullReferenceException no. In particolare a livello di API, un'eccezione ArgumentNullException chiarisce che alcuni chiamanti non hanno rispettato il contratto di un metodo e l'errore non era necessariamente un errore casuale o un problema più profondo (anche se potrebbe essere ancora così). Dovresti fallire presto.

Inoltre, quali sono i più recenti manutentori più propensi a fare? Aggiungi l'ArgumentNullException se aggiungono il codice prima del primo dereferenziamento, o semplicemente aggiungono il codice e successivamente consentono di generare una NullReferenceException?

    
risposta data 22.11.2011 - 18:43
fonte
52

I've been trained to believe that throwing the ArgumentNullException is "correct" but an "Object reference not set to an instance of an object" error means I have a bug. Why?

Supponiamo che io chiamo metodo M(x) che hai scritto. Passo nullo Ottengo un ArgumentNullException con il nome impostato su "x". Quell'eccezione significa inequivocabilmente che ho un bug; Non avrei dovuto passare null per x.

Supponiamo che chiami un metodo M che hai scritto. Passo nullo Ricevo un'eccezione null deref. Come diamine dovrei sapere se I ha un bug o tu hai un bug o qualche libreria di terze parti che hai chiamato per mio conto ha un bug? Non so se devo correggere il mio codice o chiamarti per dirti di aggiustare il tuo.

Entrambe le eccezioni sono indicative di bug; né si dovrebbe emettere mai nel codice di produzione. La domanda è: quale eccezione comunica chi ha il bug migliore alla persona che esegue il debug? L'eccezione Null Deref non ti dice quasi nulla; argomento null exception ti dice molto. Sii gentile con i tuoi utenti; genera eccezioni che consentono loro di imparare dai propri errori.

    
risposta data 22.11.2011 - 21:48
fonte
5

Il punto è che il tuo codice dovrebbe fare qualcosa di significativo.

Un NullReferenceException non è particolarmente significativo, perché potrebbe significare tutto. Senza guardare dove è stata lanciata l'eccezione (e il codice potrebbe non essere disponibile per me) non riesco a capire, cosa è andato storto.

Un ArgumentNullException è significativo, perché mi dice: l'operazione non è riuscita perché l'argomento someObject era null . Non ho bisogno di guardare il tuo codice per capirlo.

Anche sollevare questa eccezione significa che gestisci in modo esplicito null , anche se il tuo unico modo per farlo è dire che non puoi gestirlo, mentre lasciare che il codice crash possa potenzialmente significare che, potresti in realtà è in grado di gestire null, ma ha dimenticato di implementare il caso.

Il punto di eccezione è quello di comunicare le informazioni in caso di errore, che può essere gestito in fase di runtime per recuperare dall'errore, o in fase di debug per capire meglio cosa è andato storto.

    
risposta data 22.11.2011 - 19:04
fonte
2

Credo che il solo vantaggio sia che puoi fornire una diagnostica migliore nell'eccezione. Questo è il fatto che il chiamante della funzione sta passando nullo, mentre se lasci che la notifica venga lanciata, non sarai sicuro se il chiamante trasmette dati errati o se c'è un bug nella funzione stessa.

Lo farei se qualcun altro chiamerà la funzione per renderlo più facile da usare (esegui il debug se qualcosa va storto) e non farlo nel mio codice interno, perché il runtime lo controllerà comunque.

    
risposta data 22.11.2011 - 18:44
fonte
1

I've been trained to believe that throwing the ArgumentNullException is "correct" but an "Object reference not set to an instance of an object" error means I have a bug.

Hai un bug se il codice non funziona come progettato. Un NullReferenceException è lanciato dal sistema e questo fatto fa davvero più un bug che una caratteristica. Non solo perché non hai definito l'eccezione specifica da gestire. Anche perché uno sviluppatore che legge il tuo codice può presumere che non hai tenuto conto del verificarsi della situazione.

Per ragioni simili, il ApplicationException appartiene alla tua applicazione rispetto a un generale Exception che appartiene al sistema operativo. Il tipo di eccezione generata indica dove ha origine l'eccezione.

Se hai contabilizzato null, è meglio che lo gestisci con garbo lanciando un'eccezione specifica o se è un input accettabile, quindi non lanciare un'eccezione perché sono costosi. Gestirlo eseguendo operazioni con buone impostazioni predefinite o nessuna operazione (ad esempio nessun aggiornamento richiesto).

Infine, non trascurare la capacità del chiamante di gestire la situazione. Dando loro un'eccezione più specifica, ArgumentException possono costruire il loro try / catch in modo tale da gestire questo errore prima del più generale NullReferenceException che può essere causato da un numero qualsiasi di altri motivi che si verificano altrove, come un fallimento per inizializzare una variabile costruttore.

    
risposta data 22.11.2011 - 18:57
fonte
1

Il controllo rende più esplicito cosa sta succedendo, ma il vero problema si presenta con un codice come questo (forzato) esempio:

public void DoSomething(Foo someOtherObject, ISomeInterface someObject)
{
    someOtherObject.DoThisOrThat(this, someObject);
}

Qui c'è una catena di chiamate coinvolta e senza nessun controllo nullo vedi solo l'errore in someOtherObject e non dove il problema si è rivelato per la prima volta - il che significa istantaneamente debugging o interpretazione degli stack di chiamate.

In generale è meglio essere coerenti con questo tipo di cose e aggiungere sempre il controllo Null, che rende più facile individuare se ne manca uno piuttosto che scegliere e scegliere caso per caso (supponendo che tu sappia quel null è sempre non valido).

    
risposta data 22.11.2011 - 20:45
fonte
1

Un altro motivo da considerare è che cosa succede se qualche elaborazione avviene prima che l'oggetto nullo venga usato?

Supponiamo che tu abbia un file di dati degli ID delle società associate (Cid) e degli ID delle persone (Pid). Viene salvato come un flusso di coppie di numeri:

Cid Pid Cid Pid Cid Pid Cid Pid Cid Pid

E questa funzione VB scrive i record nel file:

Sub WriteRecord(Company As CompanyInfo, Person As PersonInfo)
    Using File As New StringWriter(DataFileName)
        File.Write(Company.ID)
        File.Write(Person.ID)
    End Using
End Sub

Se Person è un riferimento null, la riga File.Write(Person.ID) genererà un'eccezione. Il software può rilevare l'eccezione e recuperare, ma questo lascia un problema. Un ID azienda è stato scritto senza un ID persona associato. Se in effetti, al prossimo richiamo di questa funzione, inserirai un ID aziendale in cui era previsto l'ID della persona precedente. Così come quel record è errato, ogni record successivo avrà un ID di persona seguito da un ID di società, che è il modo sbagliato.

Cid Pid Cid Pid Cid Cid Pid Cid Pid Cid

Convalidando i parametri all'inizio della funzione, si impedisce che si verifichi un'elaborazione quando il metodo ha esito negativo.

    
risposta data 22.11.2011 - 23:14
fonte

Leggi altre domande sui tag