2009-02-23 3 views
11

Es scheint mir, dass ein Großteil meiner Debugging-Zeit darauf verwendet wird, Null-Referenz-Ausnahmen in komplexen Anweisungen zu verfolgen. Zum Beispiel:Warum kann eine Nullreferenzausnahme das Objekt, das eine Nullreferenz hat, nicht benennen?

For Each game As IHomeGame in _GamesToOpen.GetIterator() 

Warum, wenn ich eine Nullreferenceexception bekommen, kann ich die Zeilennummer in der Stack-Trace, aber nicht der Name des Objekts, das null entspricht. Mit anderen Worten, warum:

Object reference not set to an instance of an object. 

statt

_GamesToOpen is not set to an instance of an object. 

oder

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

oder

game was set to null. 

Ist das eine reine Designwahl, bedeutete die Anonymität zu schützen des Codes oder gibt es eine comp Elling Grund im Compiler-Design, diese Informationen nicht in die Debug-Time-Ausnahme aufzunehmen?

Antwort

11

Ausnahmen sind Laufzeit-Dinge, Variablen sind kompilieren Zeit Dinge.

Tatsächlich ist die Variable in Ihrem Beispiel ein Ausdruck. Ausdrücke sind nicht immer einfache Variablen. Zur Laufzeit wird der Ausdruck ausgewertet und die Methode am resultierenden Objekt aufgerufen. Wenn der Wert dieses Ausdrucks null ist, wird die Laufzeit NullReferenceException werfen. Nehmen wir folgendes:

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

Welche Fehlermeldung die Laufzeit Rückkehr sollte, wenn die GetNullValue() Methode gibt null?

+2

Zeilennummern sind auch eine Laufzeit-Sache. Die Debug-Zeit-Kompilierung enthält alle Arten von Kompilierungszeiten (Klassen- und Methodennamen, Zeilennummern usw.). Warum nicht Variablennamen? –

+1

Klassen, Methoden und Parameternamen existieren tatsächlich auf der IL-Ebene. Aber Variablen sind in der generierten IL so gut wie verschwunden. Grundsätzlich gibt es keine spezifische Möglichkeit, eine Ausnahme auf eine bestimmte Variable zu beziehen: Angenommen "if (a

+0

Akzeptiert wegen des obigen Kommentars. –

1

Eine einfache Möglichkeit, dies zum Debuggen abzufangen, um eine Assert-Anweisung zu platzieren, bevor ein Objekt verwendet wird, nach Null zu suchen und eine sinnvolle Nachricht auszugeben.

+0

Es ist nicht immer möglich, eine Assert zu haben. Beispielsweise können Sie in einem Linq-Ausdruck kein Assert hinzufügen. –

+0

Korrigiere mich, wenn ich falsch liege, aber ich dachte, Assert-Anweisungen werden aus optimierten Release-Builds entfernt. – Marc

1

In Release-Builds werden die Variablennamen aus den Symbolen entfernt und der Code könnte sogar so optimiert werden, dass er keinen bestimmten Speicherort für eine Variable enthält, sondern nur den Verweis in einem der Register (abhängig vom Bereich) der variablen Nutzung). Daher ist es möglicherweise nicht möglich, den Namen der Variablen vom Referenzstandort abzuziehen.

Im Debug-Build sind weitere Informationen zu den Variablen verfügbar. Das Ausnahmeobjekt muss jedoch auf die gleiche Weise funktionieren, unabhängig vom Build-Flavor. Daher handelt es sich um die Mindestinformationen, auf die es in jeder Geschmacksrichtung zugreifen kann.

+0

Das ist in Ordnung. Sie wollen es wahrscheinlich in Release-Builds sowieso nicht. Ich habe in der Frage angegeben, dass ich mich gefragt habe, warum Sie das in Debug-Builds nicht sehen können. –

+0

Ich habe das schon erwähnt. Die Ausnahmeobjekte sind keine Debugging-Funktion und sollten ungeachtet des Build-Flavors gleich funktionieren. Sie würden nicht erwarten, dass sich das Klassenverhalten von String radikal unter Debug-Flavor ändert, oder? –

1

Ein paar Dinge ...

1), wenn Sie Ihre eigenen Ausnahmen machen dies im Auge behalten (wenn Sie verärgert sind sie es für dieses jemand anderes wird dich ärgern, wenn Sie es für etwas anderes zu tun). Angesichts der Tatsache, dass der Ausnahmepfad überhaupt nicht der typische Pfad sein sollte, ist die Zeit, die damit verbracht wird, die Ausnahme zu machen, nützliche Informationen wert.

2) als allgemeine Programmierung practive diese Art annehmen und Sie werden weit weniger Probleme (ja Code in Bezug auf den Linien wird länger, aber Sie werden eine Menge Zeit sparen):

a) nie mach ab() .c(); do x = a.b(); x.c(); (auf separaten Zeilen), so wie Sie sehen können, war null oder wenn die Rückgabe von a.b() null ist.

b) Übergeben Sie niemals die Rückgabe eines Methodenaufrufs als Parameter - übergeben Sie immer Variablen. a (foo()); sollte sein x = foo(); Axt); Dieser ist mehr zum Debuggen und zum Sehen des Wertes.

Ich weiß nicht, warum Umgebungen wie. NET und Java keine Version der Laufzeit bieten, die mehr Informationen über diese Arten von Ausnahmen hat, z. B. was der Index in einem Array außerhalb der Grenzen war, den Namen der variablen, wenn es null ist, etc ...

2

für Sprachen wie Java, der Bytecode kompiliert werden, die von einer VM interpretiert wird, dass Sie eine Klasse X mit einem Feld x, und sein Wert ist null für eine haben bestimmte Referenz. Wenn Sie

x.foo() 

der Bytecode schreiben könnte wie folgt aussehen:

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 

Der Punkt ist, dass die Operation, die eine Nicht-Null-Referenz auf dem Stapel auf sie arbeiten muss, wie InvokeMethod im Beispiel , kann und weiß nicht, woher dieser Nullbezug stammt.

Verwandte Themen