Laden...
FAQ

[FAQ] NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt

Erstellt von herbivore vor 17 Jahren Letzter Beitrag vor 17 Jahren 204.529 Views
herbivore Themenstarter:in
49.485 Beiträge seit 2005
vor 17 Jahren
[FAQ] NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt

Hallo Ratsuchender,

hier geht es um die folgenden Exceptionmeldungen (und weiter unten generell um Tipps für den Umgang mit Exceptions):

Fehlermeldung:
System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt bzw.
System.NullReferenceException: Object reference not set to an instance to an object

Die NullReferenceException ist vermutlich die häufigste Exception überhaupt. Deshalb ist es wichtig zu lernen, wie man sie schnell selber findet und behebt. Für viele andere Exceptions kann man ganz ähnlich vorgehen.

Wie die NullReferenceException entsteht


MyObject myobj = null;
myobj.MyMethod (); // <== NullReferenceException

Wenn man auf eine Variable oder Property (dereferenzierend) zugreift, die null ist, statt auf ein Objekt zu verweisen, kommt es zu einer NullReferenceException.

Wie man die NullReferenceException behebt

Um die Exception zu beheben muss man entweder verhindern, dass der Zugriff erfolgt


MyObject myobj = null;
//...
if (myobj != null) {
   myobj.MyMethod (); // <== keine NullReferenceException
}

oder der Variable/Property ein Objekt zuweisen.


MyObject myobj = null;
//...
myobj = new MyObject ();
myobj.MyMethod (); // <== keine NullReferenceException

Also ganz einfach.

Wie man die verursachende Variable/Property findet

Jetzt muss man nur noch wissen, welche Variable/Property null ist.

Dazu guckt man sich den StackTrace der Exception an. Dieser wird normalerweise unter der Exception-Message ausgegeben. Wenn nicht, muss man nötigenfalls die Exception mit try/catch fragen (s.u.) und selbst e.StackTrace ausgeben.

Hier ist ein Beispiel für eine NullReferenceException in einem Click-EventHandler names DoClick für einen einfachen Button:

Fehlermeldung:
System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
bei MyWindow.DoClick(Object objSender, EventArgs e) in c:\herbivore\try\bspnull.cs:Zeile 38.
bei System.Windows.Forms.Control.OnClick(EventArgs e)
bei System.Windows.Forms.Button.OnClick(EventArgs e)
bei System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
bei System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
bei System.Windows.Forms.Control.WndProc(Message& m)
bei System.Windows.Forms.ButtonBase.WndProc(Message& m)
bei System.Windows.Forms.Button.WndProc(Message& m)
bei System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
bei System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
bei System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

Es interessiert normalerweise nur die erste Angabe 'bei ...'. Wie man sieht, wird hier die Zeilennummer (Zeile 38) ausgegeben (wenn die Anwendung im Debug-Modus übersetzt wurde). Man hat die Fehlerstelle also sehr schnell lokalisiert.

Sollte die Angabe der Zeile noch nicht reichen, weil dort z.B. ein komplexer Ausdruck mit vielen Variablen/Properties steht, dann kann man natürlich auch im Debugger (siehe [Artikel] Debugger: Wie verwende ich den von Visual Studio?) bis zu dieser Zeile laufen und gucken welche Variable/Property null ist.

Wenn das nicht hilft, sollte man den Ausdruck in mehrere Teilausdrücke und damit in mehrere Code-Zeilen aufteilen. Die Zwischenergebnisse sollte man dabei an Variablen zuweisen. Für diese Variablen kann man dann im Debugger prüfen, ob sie null sind.

Was bei Arrays und anderen Collections zu beachten ist

Bei Arrays können sowohl das Array also auch die Array-Elemente null sein:


MyObject [] amyobj = null
amyobj [0].MyMethod (); // <== NullReferenceException,
                        // weil das Array (amyobj) null ist

MyObject [] amyobj = new MyObject [20];
amyobj [0].MyMethod (); // <== NullReferenceException,
                        // weil das erste ArrayElement (amyobj [0]) null ist

MyObject [] amyobj = new MyObject [20];
amyobj [0] = new MyObject ():
amyobj [0].MyMethod (); // <== keine NullReferenceException

Das gleiche gilt natürlich auch für andere Collections wie z.B. List<T>.

Was bei eigenen Events zu beachten ist

Auch hinter einem Event verbirgt sich letztendlich eine Variable, die null sein kann. Sie ist null, wenn keine EventHandler für den Event registriert sind.


public event EventHandler MyEvent;

protected void OnMyEvent (EventArgs ea)
{
   if (MyEvent != null) {  // <== immer auf null abfragen, sonst gibt es
      MyEvent (this, ea);  // <== hier eine NullReferenceException, 
                           //     wenn keine Handler registriert sind
   }
}

Siehe dazu auch [Lösung] Problem mit EventHandler [==> fertige Code-Snippets].

Was bei mehreren Threads zu beachten ist

Sind mehrere Threads im Spiel und greifen diese (potenziell) gleichzeitig auf die fragliche Variable zu, kann es sein, dass die NullReferenceException nur sporadisch auftritt (Race-Condition) oder dass in dem Moment, wo man die Exception untersucht, sich der Inhalt der Variable schon wieder geändert hat. Maßgeblich für eine NullReferenceException ist der Inhalt der Variable zum Zeitpunkt des (dereferenzierenden) Zugriffs. Man muss sicherstellen, dass unabhängig von der Verzahnung der Zugriffe auf die Variable (Race-Condition), diese nie null sein kann, wenn der dereferenzierende Zugriff erfolgt. Dafür ist in aller Regel eine korrekte Synchronisation der Zugriffe (z.B. lock) erforderlich.

Was bei fremden Code zu beachten ist

Wenn die Exception in fremden Code auftritt, dann hat man bestimmt irgendwo als Parameter null übergeben und der fremde Code versucht später auf diesen Parameter zuzugreifen. In diesem Fall muss man den StackTrace solange weiterverfolgen, bis man im eigenen Code angekommen ist und dort den Fehler suchen.

Es kann natürlich auch sein, dass in dem fremden Code wirklich noch ein Bug enthalten ist. Dann kann in der Regel nur der jeweilige Hersteller die Ursache für die NullReferenceException beheben.

Was ist bei ArgumentNullException zu beachten

Im Grunde gilt hier das gleiche, nur dass die Exception nicht erst beim Zugriff auf die Variable auftritt, sondern schon bei der Übergabe der Variable (die null ist) als Parameter einer Methode oder als Wert des Setters einer Property (genauer: bei der Prüfung der Parameter/Werte).

Was ist bei anderen Exceptions zu beachten

Ganz ähnlich zu der NullReferenceException und der ArgumentNullException ist die IndexOutOfRangeException, nur dass nicht das Array oder das Element null ist, sondern der Index außerhalb des gültigen Bereichs liegt, also negativ ist oder größer gleich der Länge des Arrays. Üblicherweise ist der Index zu groß oder das Array zu klein, woraus sich normalerweise unmittelbar ergibt, was man tun muss, um den Fehler zu beheben.

Für alle Arten von Exceptions gilt, dass man die die Message sehr genau durchlesen sollte. Danach sollte man sich den StackTrace anschauen, um die Zeile zu finden, die die Exception verursacht.

Außerdem sollte man immer prüfen, ob es eine InnerException gibt. Es gibt bestimmte Exceptions (TargetInvocationException, AggregateException usw.), die stets eine InnerException haben. Eine InnerException kann selbst wieder eine eigene InnerExceptions enthalten. Erst die innerste Exception gibt Aufschluss über die eigentliche Ursache und ist daher die wichtigste Exception.

Davon abgesehen sollte man sich die genaue Situation, also insbesondere die Inhalte der relevanten Variablen, immer im Debugger anschauen. Am besten sowohl direkt bevor die Exception auftritt, als auch direkt nachdem die Exception aufgetreten ist.

Wie man eine Exception fangen kann, wenn man nicht weiß, wo sie geworfen wird

Eine Exception wird in der umgekehrten Reihenfolge, in der die Methoden einander aufgerufen haben, nach oben geworfen und zwar bis zum ersten try/catch auf das sie trifft oder - wenn es ein solches nicht gibt - bis ganz oben (unhandled exception). Wenn die Exception nicht durch ein bestehendes try/catch "weggefangen" wird, kann man die Exception also ganz oben abfangen. Ganz oben ist im Main-Thread die Main-Methode und für andere Threads deren ThreadStart-Methode bzw. deren Entsprechung, z.B. bei BackgroundWorker der DoWork-EventHandler.

Statt die Exception mit try/catch abzufangen, kann man sie auch unbehandelt lassen und die entsprechenden Events abonnieren, um ihrer habhaft zu werden, als da wären AppDomain.UnhandledException (für alle Anwendungsarten), Application.ThreadException (für Windows-Forms-Anwendungen) und Application.DispatcherUnhandledException (für WPF-Anwendungen). In [FAQ] Programm läuft in anderer Umgebung nicht (richtig) findet sich Beispielcode, um unbehandelte Exceptions zu fangen.

Man muss jedoch nicht unbedingt den Code ändern, um an die Exception zu kommen. In Visual Studio kann im Menü "Debug"/"Exceptions" eingestellt werden, dass der Debugger bei einer Exception automatisch hält. Dazu ist in "Common Language Runtime Exceptions" bei "Thrown" der Haken zu setzen. Diese Einstellung gilt für die aktuelle Solution. Tritt nun eine Exception auf, so hält der Debugger an der betreffender Stelle und über die DataTips (mit der Maus über eine Variable fahren) können die Werte der Variablen geprüft werden.

herbivore

PS: Was immer zu beachten ist

Lieber Ratsuchender, wenn du trotz der hier beschrieben Vorgehensweise den Fehler noch nicht gefunden hast oder nicht beheben konntest, mach bitte keinen neuen Thread auf, denn du hast die deutlich besseren Karten als wir. Du hast den StackTrace, du hast den gesamten Code und du kannst das Programm debuggen. Das alles haben oder können wir nicht. Also versuche bitte erst einen zweiten und dritten Anlauf.

Und wenn du dann immer noch nicht weitergekommen bist, können wir vermutlich - ohne all die Möglichkeiten, über die verfügst - erst recht nicht helfen. 😃

Siehe auch

[Artikel] Debugger: Wie verwende ich den von Visual Studio?