Objektreferenz nicht auf eine Instanz eines Objekts festgelegt. Warum zeigt .NET nicht an, welches Objekt "null" ist?

103

Zu dieser nicht behandelten .NET-Ausnahmemeldung:

Der Objektverweis wurde nicht auf eine Instanz eines Objekts festgelegt.

Warum zeigt .NET nicht an, welches Objekt es ist null?

Ich weiß, dass ich nullden Fehler suchen und beheben kann. Warum hilft .NET jedoch nicht dabei, darauf hinzuweisen, welches Objekt eine Nullreferenz hat und welcher Ausdruck die ausgelöst hat NullReferenceException?


quelle
2
Wenn dies passiert, schreiben Sie die Zeile, in der es passiert ist, neu, damit jedes mögliche Ergebnis zuerst auf Null überprüft wird - dann wissen Sie genau, was es war. Entweder das, oder Sie haben den erstaunlichen Debugger von Visual Studio angehängt, der den Moment bricht, in dem eine Ausnahme auftritt, und Sie sehen lässt, was null ist :)
Patashu
5
Nicht wirklich, er fragt einfach, warum das .NET Framework dem Programmierer nicht hilft, zu zeigen, welches Objekt null ist. Ich denke, es ist die Leistungsstrafe (Sie müssten nachdenken). aber ich bin mir auch nicht sicher.
Bas
1
@bas: Während das stimmt, ist die Frage insofern etwas irreführend, als sie nach einem "Teil eines Ausdrucks" und nicht nach einem "Objekt" fragen sollte. Dies erklärt auch, warum bloße Reflexion nicht hilft, aber einige umfangreiche Debug-Informationen erforderlich sind.
ODER Mapper
4
Trotzdem bin ich neugierig auf die Antwort. Es ähnelt den .net-Ausnahmen und hilft nicht, darauf hinzuweisen, welcher Schlüssel in einem Wörterbuch nicht vorhanden ist. Außerdem verstehe ich die Anhänger der Frage nicht.
Bas
12
Terminologie bitte: Ein Objekt ist niemals null. Eine Objektreferenz könnte jedoch sein. Eine Objektreferenz ist jedoch nur ein Speicherort - wie würde sie Ihnen helfen, wenn Sie keinen Debugger haben?
Oskar Berggren

Antworten:

169

(Informationen zum neuen Ausnahme-Helfer in Visual Studio 2017 finden Sie am Ende dieser Antwort.)


Betrachten Sie diesen Code:

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

Dies wirft ein NullReferenceExceptionin die zweite Zeile und Sie möchten wissen, warum .NET Ihnen nicht sagt, dass es snull war, als die Ausnahme ausgelöst wurde.

Um zu verstehen, warum Sie diese Informationen nicht erhalten, sollten Sie sich daran erinnern, dass nicht die C # -Quelle ausgeführt wird, sondern IL:

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

Es ist der callvirtOpcode, der das auslöst, NullReferenceExceptionund das tut es, wenn das erste Argument auf dem Auswertungsstapel eine Nullreferenz ist (die, die mit geladen wurde ldloc.0).

Wenn .NET erkennen soll, dass es sich sum eine Nullreferenz handelt, sollte es in gewisser Weise nachverfolgen, dass das erste Argument auf dem Evaluierungsstapel aus einer Form stammt s. In diesem Fall ist es für uns leicht zu erkennen, sdass es null war, aber was ist, wenn der Wert ein Rückgabewert eines anderen Funktionsaufrufs war und nicht in einer Variablen gespeichert wurde? Auf jeden Fall ist diese Art von Informationen nicht das, was Sie in einer virtuellen Maschine wie der virtuellen .NET-Maschine verfolgen möchten.


Um dieses Problem zu vermeiden, schlage ich vor, dass Sie in allen öffentlichen Methodenaufrufen eine Argument-Null-Prüfung durchführen (es sei denn, Sie erlauben natürlich die Null-Referenz):

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

Wenn null an die Methode übergeben wird, erhalten Sie eine Ausnahme, die genau beschreibt, wo das Problem liegt ( sdh null).


Vier Jahre später hat Visual Studio 2017 jetzt einen neuen Ausnahme-Helfer, der versucht zu erkennen, was null ist, wenn a NullReferenceExceptionausgelöst wird. Es kann Ihnen sogar die erforderlichen Informationen geben, wenn es sich um den Rückgabewert einer Methode handelt, die null ist:

Ausnahmehilfe für Visual Studio 2017

Beachten Sie, dass dies nur in einem DEBUG-Build funktioniert.

Martin Liversage
quelle
5
Zeilennummern und Quelldateinamen werden auch nicht im IL-Code selbst gespeichert, oder? Sie können jedoch zum Debuggen zur Verfügung gestellt werden.
ODER Mapper
4
@ MartinLiversage: Genau. So ist die Frage nach läuft darauf hinaus: Warum gibt es nicht genügend Dateien in den Symbolen gespeicherten Informationen , die , was Ausdruck wurde im Code ausgewertet erzählt auch null.
ODER Mapper
2
@MartinLiversage: Auf Symboldateien kann so zugegriffen werden, dass bei einer Ausnahme zusammen mit der Ausnahmemeldung die Quelldatei und die Zeilennummer in der Ausgabe des Debuggers angezeigt werden können. Die Frage ist also, was der Grund dafür ist, dass keine weiteren Informationen darüber enthalten sind, was genau zurückgegeben wurde. nullBeachten Sie, dass das OP nicht behauptet, dass er oder sie wissen möchte, dass Debug-Builds auch für Release-Builds ausreichend sein können.
ODER Mapper
3
Hmm, und wir sollten nicht vergessen, dass nicht einmal die IL tatsächlich ausgeführt wird, sondern nativer Code, der zur Laufzeit daraus erstellt wird.
Oskar Berggren
2
@MartinLiversage: Niemand in dieser Frage behauptet, dass dies für Release-Builds perfekt unterstützt werden soll. Wie auch immer, ich sehe das Problem nicht ganz darin, den IL-Opcode, der eine Objektreferenz verwendet (die sich möglicherweise herausstellt null), mit der Zeile und Spalte der Quelldatei zu korrelieren, die diese Objektreferenz zurückgegeben hat.
ODER Mapper
9

Wie soll die Fehlermeldung im folgenden Fall aussehen?

AnyObject.GetANullObject().ToString();

private object GetANullObject()
{
  return null;
}

Hier gibt es keine Variablennamen zu melden!

Romar
quelle
2
Ich vermute, das OP sucht nach dem Ausdruck im Quellcode, der null zurückgibt, nicht nach dem Objekt. Ich habe der Frage einen entsprechenden Kommentar hinzugefügt und hoffe, dass er oder sie die Dinge klären wird. Wenn mein Verdacht richtig ist, würde das OP so etwas wie Object reference obtained from AnyObject.GetANullObject() not set to an instance of an object.eine Fehlermeldung erwarten .
ODER Mapper
1
@ ORMapper Ich stimme zu. Ich hätte meine "Antwort" einfach in einen Kommentar zum OP eingetragen, wenn ich genug Reputationspunkte gehabt hätte, um einen Kommentar hinzuzufügen!
Romar
1
"Eine Nullreferenz, die versucht hat, die ToString () -Methode der Klasse XYZ aufzurufen" wäre hilfreicher als das, was wir jetzt erhalten.
Michael Levy
Am hilfreichsten wäre eine Stapelverfolgung, die auf jeder Aufrufebene genau anzeigt, welche Zeile in welcher Datei zu dem Fehler geführt hat. Oh, warte ... das macht es jetzt!
Jim Balter
1

Nun, das müssen die Ingenieure von Microsoft beantworten. Aber Sie können natürlich einen Debugger verwenden und watch hinzufügen, um herauszufinden, welche davon ein Problem haben.

Die Ausnahme ist jedoch NullReferenceException, dass die Referenz nicht vorhanden ist . Sie können das Objekt nicht erhalten, das überhaupt nicht erstellt wurde.

but why .NET don't tell us which object is null? Weil es nicht weiß, welches Objekt null ist. Das Objekt existiert einfach nicht!

Gleiches gilt, wenn ich sage, dass C # zu .NET IL-Code kompiliert wurde. Der .NET IL-Code kennt die Namen oder Ausdrücke nicht. Es kennt nur Referenzen und deren Standort. Auch hier kann man nicht bekommen, was nicht existiert. Der Ausdruck oder der Variablenname existiert nicht.

Philosophie: Sie können kein Omelett machen, wenn Sie überhaupt kein Ei haben.

Aniket Inge
quelle
3
Das ist auch keine Antwort :)
Bas
Wie erhalten Sie die Referenz, wenn sie nicht vorhanden ist? @ Bas
Aniket Inge
4
"Nun, das müssen die Ingenieure von Microsoft beantworten." Lassen Sie sie also ein Licht darauf werfen, anstatt das Offensichtliche zu sagen
bas
@bas können wir entscheiden, was zumindest logisch ist. Logischerweise existiert das Objekt nicht. Wie werden Sie es mit einer Ausnahme abfangen und dann den Namen des Objekts ausdrucken? Es existiert einfach nicht. Nicht einmal auf Stapel ..
Aniket Inge
Die Antwort ist also, dass es praktisch unmöglich ist, darauf hinzuweisen, welches Objekt eine Nullreferenz hat. Das ist auch eine Antwort. Ich sage nicht, dass ich weiß, ich mag nur die Frage :). +1 für alle Bemühungen: p
bas
1

Nicht sicher, aber dies kann daran liegen, dass .Net nicht weiß, ob es sich um eine vordefinierte Klasse oder eine benutzerdefinierte Klasse handelt. Wenn es vordefiniert ist, kann es null sein (wie eine Zeichenfolge, die 2 Bytes belegt), aber wenn es benutzerdefiniert ist, müssen wir eine Instanz davon erstellen, damit es weiß, dass dieses Objekt so viel Speicher belegt. Daher wird zur Laufzeit ein Fehler ausgegeben.

Oniel Telies
quelle
-2

Gute Frage. Das Meldungsfeld ist nur knapp nutzlos. Selbst wenn es eine Meile tief von der Referenzdefinition entfernt ist, wären einige Klassen, Baugruppen, Dateien oder andere Informationen besser als das, was sie derzeit bereitstellen (sprich: besser als nichts).

Am besten führen Sie es im Debugger mit Debugging-Informationen aus, und Ihre IDE wird an der betreffenden Zeile unterbrochen (was ziemlich deutlich zeigt, dass nützliche Informationen tatsächlich verfügbar sind).

Rick O'Shea
quelle