2009-02-23 8 views
11

Mi sembra che gran parte del mio tempo di debug sia dedicato all'inseguimento di eccezioni di riferimento null in istruzioni complesse. Per esempio:Perché un'eccezione di riferimento null non può denominare l'oggetto con riferimento null?

For Each game As IHomeGame in _GamesToOpen.GetIterator() 

Perché, quando ricevo un NullReferenceException, posso ottenere il numero di riga nella traccia dello stack, ma non il nome dell'oggetto che è uguale a zero. In altre parole, il motivo per cui:

Object reference not set to an instance of an object. 

invece di

_GamesToOpen is not set to an instance of an object. 

o

Anonymous object returned by _GamesToOpen.GetIterator() is null. 

o

game was set to null. 

E` strettamente una scelta progettuale, destinate a proteggere l'anonimato del codice o c'è un comp motivo di elling nella progettazione del compilatore per non includere questa informazione nell'eccezione di debug-time?

risposta

11

Le eccezioni sono cose in fase di esecuzione, le variabili sono cose in fase di compilazione.

Infatti, la variabile nell'esempio è un'espressione. Le espressioni non sono sempre semplici variabili. In fase di esecuzione, l'espressione verrà valutata e il metodo verrà chiamato sull'oggetto risultante. Se il valore di tale espressione è null, il runtime genererà un valore NullReferenceException. Assumere i seguenti:

Dim a as New MyObject 
Dim b as String = MyObject.GetNullValue().ToString() 

Quale messaggio di errore dovrebbe il ritorno runtime se il metodo restituisce GetNullValue()null?

+2

I numeri di linea sono anche una cosa da runtime. La compilazione debug-time contiene tutti i tipi di cose in fase di compilazione (nomi di classi e metodi, numeri di riga, ecc.) Perché non i nomi delle variabili? –

+1

Classi e metodi e nomi di parametri esistono effettivamente a livello IL. Ma le variabili sono praticamente andate nell'IL generato. Fondamentalmente, non esiste un modo specifico per mettere in relazione un'eccezione con una variabile specifica: Assumi "if (a

+0

Accettato a causa del commento sopra. –

1

Un modo semplice per rilevare ciò per eseguire il debug di inserire un'istruzione Assert prima di utilizzare un oggetto, verificare null e generare un messaggio significativo.

+0

Non è sempre possibile avere un Assert. Ad esempio, non è possibile aggiungere un'asserzione all'interno di un'espressione Linq. –

+0

Correggimi se ho torto, ma ho pensato che le dichiarazioni di Assert sono state rimosse dalle build di Release ottimizzate. – Marc

1

Nelle versioni di rilascio, i nomi delle variabili vengono rimossi dai simboli e il codice potrebbe anche essere ottimizzato per non avere una posizione di memoria specifica per una variabile, ma solo mantenere il riferimento in uno dei registri (a seconda dello scopo dell'uso della variabile). Pertanto, potrebbe non essere possibile dedurre il nome della variabile dalla posizione di riferimento.

Nella compilazione di debug, sono disponibili ulteriori informazioni sulle variabili. Tuttavia, l'oggetto dell'eccezione deve funzionare allo stesso modo, indipendentemente dal sapore della build. Quindi, agisce sull'informazione minima a cui può accedere in qualsiasi sapore.

+0

Va bene. Probabilmente non lo vuoi in build di release in ogni caso. Ho specificato nella domanda che mi stavo chiedendo il motivo per cui non riesci a vederlo nelle build di debug. –

+0

Anche se ho menzionato questo. Gli oggetti di eccezione non sono una funzionalità di debug e dovrebbero funzionare allo stesso modo indipendentemente dal gusto della build. Non ti aspetteresti che il comportamento della classe String cambi radicalmente sotto Debug, vero? –

1

Un paio di cose ...

1) quando si effettua le proprie eccezioni tenere questo in mente (se si è infastiditi che per questo qualcun altro sarà seccato di voi se lo si fa per qualcos'altro). Dato che il percorso dell'eccezione non dovrebbe essere affatto il percorso tipico, il tempo speso a fare in modo che l'eccezione abbia informazioni utili vale la pena.

2) come practive programmazione generale adottare questo stile e si avrà molto meno problemi (sì il vostro codice sarà più lungo in termini di linee, ma si farà risparmiare un sacco di tempo):

a) mai do ab(). c(); fai x = a.b(); X.c(); (su righe separate) in questo modo è possibile vedere di un valore null o se il ritorno di a.b() è nullo.

b) non passare mai il ritorno di una chiamata di metodo come parametro - passare sempre le variabili. un (foo()); dovrebbe essere x = foo(); ascia); Questo è più per il debug e la possibilità di vedere il valore.

Non so perché ambienti come .net e Java non forniscono una versione del runtime che ha più informazioni su questi tipi di eccezioni, come ad esempio l'indice era su un array fuori limite, il nome della variabile quando è nullo, ecc ...

2

per linguaggi come Java che vengono compilati in bytecode che viene interpretato da un VM, si supponga di avere una classe X con un campo x, e il suo valore è null per un certo riferimento Se si scrive

x.foo() 

il bytecode potrebbe assomigliare a questo:

push Xref   >> top of stack is ref to instance of X with X.x = null 
getField x   >> pops Xref, pushes 'null' on the stack 
invokeMethod foo >> pops 'null' -> runtime exception 

Il punto è che l'operazione che ha bisogno di un punto di riferimento non nullo sullo stack di operare su di esso, come invokeMethod nell'esempio , non può e non sa da dove provenga quel riferimento nullo.