Riferimento a un oggetto non impostato su un'istanza di un oggetto. Perché .NET non mostra quale oggetto è "null"?


103

Per quanto riguarda questo messaggio di eccezione non gestito .NET:

Il riferimento non impostato su un'istanza di un oggetto.

Perché .NET non mostra quale oggetto è null ?

So di poter verificare nulle risolvere l'errore. Tuttavia, perché .NET non aiuta a sottolineare quale oggetto ha un riferimento nullo e quale espressione ha attivato il NullReferenceException?

c#  .net 

2
Quando ciò accade, riscrivi la riga in cui è successo in modo che controlli prima ogni possibile risultato per null, quindi saprai esattamente di cosa si trattava. O quello, o avere collegato il fantastico debugger di Visual Studio, che interrompe l'istante in cui si verifica un'eccezione e ti consente di vedere cosa è nullo :)
Patashu

5
Non proprio, chiede semplicemente perché il framework .NET non aiuta il programmatore a mostrare quale oggetto è nullo. Immagino sia la penalizzazione delle prestazioni (avresti bisogno di riflessione). ma non ne sono nemmeno sicuro.
sabato

1
@bas: Anche se questo è vero, la domanda è un po 'fuorviante in quanto dovrebbe riguardare una "parte di un'espressione", non un "oggetto". Questo spiega anche perché la semplice riflessione non aiuta, ma saranno necessarie alcune informazioni di debug estese.
OR Mapper

4
Sono ancora curioso della risposta. È un po 'simile alle eccezioni .net che non aiutano a indicare quale chiave non esiste in un dizionario. Inoltre, non capisco i devoti sulla domanda.
sabato

12
Terminologia per favore: un oggetto non è mai nullo. Potrebbe esserci un riferimento a un oggetto . Ma un riferimento a un oggetto è solo una posizione in memoria: come ti aiuterebbe, a meno che tu non abbia comunque un debugger collegato?
Oskar Berggren,

Risposte:


169

(Per informazioni sul nuovo helper di eccezioni in Visual Studio 2017, vedere la fine di questa risposta)


Considera questo codice:

String s = null;
Console.WriteLine(s.Length);

Questo lancerà un NullReferenceExceptionnella seconda riga e vuoi sapere perché .NET non ti dice che lo eras che era nulla quando è stata lanciata l'eccezione.

Per capire perché non ottieni quell'informazione dovresti ricordare che non è l'origine C # che viene eseguita ma piuttosto IL:

IL_0001: ldnull      
IL_0002: stloc.0 // s
IL_0003: ldloc.0 // s
IL_0004: callvirt System.String.get_Length
IL_0009: chiama System.Console.WriteLine

È il callvirtcodice operativo che lancia il NullReferenceExceptione lo fa quando il primo argomento sullo stack di valutazione è un riferimento nullo (quello che è stato caricato utilizzando ldloc.0).

Se .NET dovesse essere in grado di dire che si trattava di sun riferimento nullo, dovrebbe in qualche modo tenere traccia che il primo argomento nello stack di valutazione ha avuto origine dal modulo s. In questo caso è facile per noi vedere che è snullo, ma cosa succederebbe se il valore fosse un valore restituito da un'altra chiamata di funzione e non memorizzato in alcuna variabile? Ad ogni modo, questo tipo di informazioni non è ciò di cui vuoi tenere traccia in una macchina virtuale come la macchina virtuale .NET.


Per evitare questo problema ti suggerisco di eseguire il controllo null degli argomenti in tutte le chiamate ai metodi pubblici (a meno che, ovviamente, non permetti il ​​riferimento null):

public void Foo(String s) {
  if (s == null)
    throw new ArgumentNullException("s");
  Console.WriteLine(s.Length);
}

Se al metodo viene passato null, si ottiene un'eccezione che descrive con precisione qual è il problema (ovvero snull).


Quattro anni dopo, Visual Studio 2017 ha ora un nuovo helper per le eccezioni che proverà a indicare cosa è null quando NullReferenceExceptionviene generato un. È anche in grado di darti le informazioni richieste quando è il valore di ritorno di un metodo che è nullo:

Assistente eccezioni di Visual Studio 2017

Nota che questo funziona solo in una build DEBUG.


5
I numeri di riga ei nomi dei file di origine non sono memorizzati nel codice IL stesso, o lo sono? Tuttavia, possono essere resi disponibili per il debug.
O Mapper

4
@ MartinLiversage: esatto. Quindi la domanda si riduce a: Perché non ci sono informazioni sufficienti memorizzate nei file di simboli che dicono anche quale espressione nel codice è stata valutata null.
OPPURE Mapper

2
@MartinLiversage: i file dei simboli sono accessibili in modo tale che in caso di eccezione, insieme al messaggio di eccezione, il file di origine e il numero di riga possano essere visualizzati nell'output del debugger. Quindi, la domanda è: qual è la ragione per non includere alcune informazioni in più su ciò che è stato restituito esattamente null- nota che l'OP non afferma di voler sapere che anche per le build di rilascio, le build di debug potrebbero essere sufficienti.
OPPURE Mapper

3
Hmm, e non dovremmo dimenticare che non è nemmeno l'IL che viene effettivamente eseguito, ma piuttosto il codice nativo costruito da esso in fase di esecuzione.
Oskar Berggren,

2
@MartinLiversage: Nessuno in questa domanda afferma di volerlo perfettamente supportato per le build di rilascio. Ad ogni modo, non vedo il problema nel correlare il codice operativo IL che utilizza un riferimento a un oggetto (che potrebbe rivelarsi essere null) con la riga e la colonna del file di origine che ha restituito quel riferimento all'oggetto.
OR Mapper

9

Come vuoi che appaia il messaggio di errore nel seguente caso?

AnyObject.GetANullObject().ToString();

private object GetANullObject()
{
  return null;
}

Nessun nome di variabile da riportare qui!


2
Sospetto che l'OP stia cercando l'espressione nel codice sorgente che restituisce null, non l'oggetto. Ho aggiunto un rispettivo commento alla domanda e spero che lui o lei chiarirà le cose. Se il mio sospetto è corretto, l'OP si aspetterebbe qualcosa di simile Object reference obtained from AnyObject.GetANullObject() not set to an instance of an object.al messaggio di errore.
O Mapper

1
@ORMapper Sono d'accordo. Avrei semplicemente messo la mia "risposta" in un commento all'OP, se avessi avuto abbastanza punti reputazione per aggiungere un commento!
romar

1
"un riferimento nullo ha tentato di chiamare il metodo ToString () della Classe XYZ" sarebbe più utile di quello che otteniamo ora.
Michael Levy

La cosa più utile sarebbe una traccia dello stack che mostra, a ogni livello di chiamata, esattamente quale riga, in quale file, ha provocato l'errore. Oh, aspetta ... è quello che fa ora!
Jim Balter

1

Beh, spetta agli ingegneri di Microsoft rispondere. Ma puoi ovviamente usare un debugger e aggiungere watch per scoprire quale di questi ha un problema.

Tuttavia, l'eccezione è NullReferenceExceptionche significa che il riferimento non esiste . Non puoi ottenere l'oggetto che non è stato creato affatto.

but why .NET don't tell us which object is null? Perché non sa quale oggetto è nullo. L'oggetto semplicemente non esiste!

Lo stesso è il caso quando dico che C # è compilato in codice .NET IL. Il codice .NET IL non conosce i nomi o le espressioni. Conosce solo i riferimenti e la loro posizione. Anche qui non puoi ottenere ciò che non esiste. L'espressione o il nome della variabile non esiste.

Filosofia: non puoi fare una frittata se non hai un uovo in primo luogo.


3
che non è una risposta sia :)
bas

Come si ottiene il riferimento se non esiste? @Bas
Aniket Inge,

4
"Beh, spetta agli ingegneri di Microsoft rispondere.". Quindi lascia che facciano luce su questo invece di affermare l'ovvio
bas

@bas possiamo decidere almeno cosa è logico. Logicamente l'oggetto non esiste. Come farai a catturarlo con un'eccezione e quindi stampare il nome dell'oggetto? Semplicemente non esiste. Nemmeno in pila ..
Aniket Inge

quindi la risposta è che è praticamente impossibile indicare quale oggetto ha un riferimento nullo? Anche questa è una risposta. Non sto affermando che lo so, mi piace solo la domanda :). +1 per tutto lo sforzo: p
bas

1

Non sono sicuro, ma potrebbe essere perché .Net non sa se si tratta di una classe predefinita o definita dall'utente. Se è predefinito, può essere nullo (come una stringa che occupa 2 byte) ma se è definito dall'utente dobbiamo crearne un'istanza in modo che sappia che questo oggetto occuperà così tanta memoria. Quindi quindi genera un errore in fase di esecuzione.


-2

Buona domanda. La finestra del messaggio è solo inutile. Anche se è sepolto a un miglio di profondità dalla definizione dei riferimenti, alcune classi o assembly o file o altre informazioni sarebbero migliori di quelle che forniscono attualmente (leggi: meglio di niente).

La tua migliore opzione è eseguirlo nel debugger con le informazioni di debug e il tuo IDE si interromperà sulla linea incriminata (dimostrando piuttosto chiaramente che le informazioni utili sono effettivamente disponibili).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.