Perché "il riferimento all'oggetto non è impostato su un'istanza di un oggetto" non ci dice quale oggetto?

36

Stiamo lanciando un sistema e talvolta otteniamo la famosa eccezione NullReferenceException con il messaggio Object reference not set to an instance of an object .

Tuttavia, in un metodo in cui abbiamo quasi 20 oggetti, avere un log che dice che un oggetto è nullo, è davvero inutile. È come dirti, quando sei l'agente di sicurezza di un seminario, che un uomo tra i 100 partecipanti è un terrorista. Questo è davvero inutile per te. Dovresti ottenere maggiori informazioni, se vuoi scoprire quale uomo è l'uomo minaccioso.

Allo stesso modo, se vogliamo rimuovere il bug, dobbiamo sapere quale oggetto è nullo.

Ora, qualcosa mi ha ossessionato per diversi mesi, e cioè:

Perché .NET non ci fornisce il nome, o almeno il tipo di riferimento all'oggetto, che è null? . Non riesci a capire il tipo dal riflesso o da qualsiasi altra fonte?

Inoltre, quali sono le migliori pratiche per capire quale oggetto è nullo? Dovremmo verificare sempre il nullability degli oggetti in questi contesti manualmente e registrare il risultato? C'è un modo migliore?

Aggiornamento: L'eccezione The system cannot find the file specified ha la stessa natura. Non è possibile trovare il file, finché non si allega al processo e si esegue il debug. Immagino che questi tipi di eccezioni possano diventare più intelligenti. Non sarebbe meglio se .NET potesse dirci c:\temp.txt doesn't exist. invece di quel messaggio generale? Come sviluppatore, voto di sì.

    
posta Saeed Neamati 15.01.2012 - 12:20
fonte

7 risposte

28

Il NullReferenceException ti dice fondamentalmente: stai sbagliando. Niente di più, niente di meno. Non è uno strumento di debug completo, al contrario. In questo caso, direi che lo stai facendo male sia perché

  • esiste una NullReferenceException
  • non l'hai impedito in un modo in cui sai perché / dove è successo
  • e forse anche: un metodo che richiede 20 oggetti sembra un po 'fuori

Sono un grande fan del controllo di tutto prima che le cose inizino a non funzionare correttamente e forniscano buone informazioni allo sviluppatore. In breve: scrivi assegni usando ArgumentNullException e i mi piace e scrivi il nome tu stesso. Ecco un esempio:

void Method(string a, SomeObject b)
{
    if (a == null) throw ArgumentNullException("a");
    if (b == null) throw ArgumentNullException("b");

    // See how nice this is, and what peace of mind this provides? As long as
    // nothing modifies a or b you can use them here and be 100% sure they're not
    // null. Should they be when entering the method, at least you know which one
    // is null.
    var c = FetchSomeObject();
    if(c == null)
    {
        throw InvalidOperationException("Fetching B failed!!");
    }

    // etc.
}

Potresti anche consultare Contratti di codice , ha delle stranezze ma funziona piuttosto bene e ti consente di digitare un po '.

    
risposta data 15.01.2012 - 12:43
fonte
17

Dovrebbe davvero mostrare esattamente quello che stai cercando di chiamare. È come dire "C'è un problema, devi risolverlo, so cosa è, non te lo dirò, vai a capirlo". Un po 'come la metà delle risposte su questo stack overflow, ironia della sorte.

Quindi, quanto sarebbe utile, ad esempio, se tu avessi questo ...

Object reference (HttpContext.Current) not set to instance of an object

...? Per entrare nel codice, attraversarlo e capire che la cosa che stai cercando di chiamare è null va bene, ma perché non darci un piccolo aiuto?

Sono d'accordo che è di solito utile per passare attraverso il codice per ottenere la risposta (perché probabilmente ne scoprirai di più), ma spesso un sacco di tempo e frustrazione verrebbero salvati se il NullReferenceException text era più simile all'esempio sopra.

Basta dire.

    
risposta data 26.10.2012 - 09:17
fonte
4

Il registro dovrebbe includere una traccia dello stack, che di solito fornisce un suggerimento su quale riga nel metodo ha il problema. Potrebbe essere necessario fare in modo che la build di rilascio includa i simboli PDB in modo da avere un'idea su quale riga è presente l'errore.

Certo, non ti aiuterà in questo caso:

Foo.Bar.Baz.DoSomething()

Il tell non chiedere può aiutare ad evitare tale codice.

Per quanto riguarda il motivo per cui l'informazione non è inclusa, non sono sicuro - sospetto che almeno in una build di debug, se davvero volessero, potrebbero capirlo. Prendere una discarica e aprire WinDBG può aiutare.

    
risposta data 15.01.2012 - 12:33
fonte
2

Le eccezioni sono state create come uno strumento per segnalare condizioni non fatali eccezionali nella catena di chiamate. Cioè, non sono progettati come uno strumento di debug.

Se un'eccezione a puntatore nullo fosse uno strumento di debug, avrebbe interrotto l'esecuzione del programma direttamente sul posto, consentendo a un debugger di connettersi, puntandolo direttamente sulla linea compromettente. Ciò fornirebbe al programmatore tutte le informazioni di contesto disponibili. (Che è praticamente ciò che un Segfault dovuto all'accesso di un puntatore nullo fa in C, anche se un po 'rozzo.)

Tuttavia, l'eccezione del puntatore nullo è progettata come una condizione di runtime valida che può essere generata e catturata nel normale flusso del programma. Di conseguenza, è necessario tenere conto delle considerazioni sulle prestazioni. E ogni personalizzazione del messaggio di eccezione richiede che gli oggetti stringa vengano creati, concatenati e distrutti in fase di runtime. In quanto tale, un messaggio statico è indiscutibilmente più veloce.

Non sto dicendo che il runtime non potrebbe essere programmato in un modo che potrebbe dare il nome del riferimento incriminante, però. Questo potrebbe essere fatto. Renderebbe le eccezioni ancora più lente di quanto non siano. Se a qualcuno importava abbastanza, una tale funzione poteva anche essere resa commutabile, in modo da non rallentare il codice di produzione, ma consentire un debugging più semplice; ma per qualche motivo a nessuno sembra esserci curato abbastanza.

    
risposta data 21.08.2015 - 23:14
fonte
1

Cromulento ha colpito il chiodo sulla testa, penso, tuttavia c'è anche il punto ovvio che se stai ottenendo un NullReferenceException hai una o più variabili non inizializzate. L'argomento secondo cui hai passato circa 20 oggetti in un metodo non può essere considerato una limitazione: come creatore di una porzione di codice devi essere responsabile delle sue azioni, che include la sua conformità al resto di una base di codice, come così come l'uso corretto e corretto delle variabili ecc.

è oneroso, noioso e talvolta noioso, ma i premi alla fine ne valgono la pena: molte sono le volte in cui ho dovuto trascinare attraverso i file di registro che pesano diversi gigabyte, e sono quasi sempre utili. Tuttavia, prima di arrivare a quel punto, il debugger può aiutarti, e prima di quella fase una buona pianificazione risparmierà molto dolore (e non intendo neanche un approccio completamente ingegnerizzato alla tua soluzione di codice: semplici schizzi e alcune note possono e vogliono essere meglio di niente).

Riguardo al Object reference not set to an instance of an object il codice non può indovinare valori che potrebbero piacerti: questo è il nostro lavoro di programmatori e significa semplicemente che hai passato una variabile non inizializzata in.

    
risposta data 01.02.2013 - 11:18
fonte
0

Impara a usare il debugger. Questo è esattamente il tipo di cosa per cui è stato progettato. Imposta un punto di interruzione sul metodo in questione e via.

Basta scorrere il codice e vedere esattamente quali sono i valori di tutte le tue variabili in determinati punti.

Modifica: Francamente sono scioccato dal fatto che nessun altro abbia menzionato l'utilizzo del debugger.

    
risposta data 26.10.2012 - 13:57
fonte
0

Se vuoi seguire la risposta di @ stijn e inserire controlli nulli nel codice, questo frammento di codice dovrebbe essere d'aiuto. Ecco alcune informazioni sugli snippet di codice . Una volta impostato, digita argnull , premi due volte la scheda, quindi riempi lo spazio vuoto.

<CodeSnippet Format="1.0.0">
  <Header>
    <Title>EnsureArgNotNull</Title>
    <Shortcut>argnull</Shortcut>
  </Header>
  <Snippet>
    <Declarations>
      <Literal>
        <ID>argument</ID>
        <ToolTip>The name of the argument that shouldn't be null</ToolTip>
        <Default>arg</Default>
      </Literal>
    </Declarations>
    <Code Language="CSharp">
      <![CDATA[if ($argument$ == null) throw new ArgumentNullException("$argument$");$end$]]>
    </Code>
  </Snippet>
</CodeSnippet>
    
risposta data 21.08.2015 - 22:29
fonte

Leggi altre domande sui tag