Debuggen für Einsteiger
Der Code, den wir als Softwareentwickler schreiben, verhält sich zweifellos nicht immer wie erwartet. Manchmal verhält er sich sogar völlig anders. Wenn das Unerwartete auftritt, müssen Sie herausfinden, warum der Code sich anders verhält. Hierfür können Sie den Code zwar aufwändig durchgehen, es ist jedoch einfacher und effizienter, ein Tool dafür zu verwenden – den Debugger.
Ein Debugger kann jedoch leider nicht alle Probleme oder Fehler im Code erkennen. Debuggen bedeutet, dass Sie Ihren Code schrittweise in einem entsprechenden Tool wie Visual Studio ausführen, um die Stelle zu finden, an der Sie einen Fehler beim Programmieren gemacht haben. Dann können Sie entscheiden, welche Korrekturen Sie am Code vornehmen müssen. In vielen Debuggern können Sie temporäre Änderungen vornehmen und mit der Ausführung des Programms fortfahren.
Es erfordert Zeit und Übung, bis Sie den Debugger effektiv einsetzen können, letztendlich handelt es sich dabei allerdings um eine der wichtigsten Aufgaben eines Softwareentwicklers. In diesem Artikel werden Sie in die wichtigsten Aspekte des Debuggens eingeführt und erhalten Tipps, die Ihnen den Einstieg erleichtern.
Ermitteln des Problems durch Stellen der richtigen Fragen
Es ist wichtig, das Problem zu ermitteln, bevor Sie versuchen, es zu beheben. In Ihrem Code sind in der Vergangenheit sicherlich bereits Probleme aufgetreten, weshalb Sie sich nun in das Debuggen einlesen. Bevor Sie mit dem Debuggen beginnen, sollten Sie sicherstellen, dass Sie das Problem ermittelt haben, das Sie lösen möchten:
Wie sollte sich der Code eigentlich verhalten?
Wie hat er sich stattdessen verhalten?
Wenn beim Ausführen Ihrer App ein Fehler (bzw. eine Ausnahme) aufgetreten ist, kann das sogar von Vorteil sein! Bei einer Ausnahme handelt es sich um ein unerwartetes Ereignis, das beim Ausführen von Code auftritt – üblicherweise ein Fehler. Ein Debugger kann Sie an die Stelle im Code führen, an der die Ausnahme aufgetreten ist, und Sie dabei unterstützen, eine Lösung zu finden.
Wie wirkt sich das unerwartete Problem aus? Haben Sie bereits einen Verdacht, an welcher Stelle im Code das Problem aufgetreten ist? Wenn Ihr Code beispielsweise Text anzeigt, der Text aber falsch ist, wissen Sie, dass ein Fehler in Ihren Daten vorliegt oder der Code, mit dem der Anzeigetext festgelegt wurde, weist einen Fehler auf. Wenn Sie den Code mit einem Debugger schrittweise durchlaufen, können Sie jede Änderung an Ihren Variablen untersuchen, um herauszufinden, wann und wie falsche Werte zugewiesen wurden.
Überprüfen Ihrer Annahmen
Bevor Sie einen Fehler untersuchen, überdenken Sie die Annahmen, die dazu geführt haben, dass Sie ein bestimmtes Ergebnis erwarten. Unbekannte Annahmen können beim Ermitteln eines Problems im Weg stehen, auch wenn Sie die Ursache des Problems bereits im Debugger gefunden haben. Die Liste Ihrer Annahmen kann sehr lang sein! Im Folgenden finden Sie einige Fragen, die Sie sich stellen sollten, um Ihre Annahmen auf die Probe zu stellen.
Verwenden Sie die richtige API (also die richtigen Objekte, Funktionen, Methoden oder Eigenschaften)? Die API, die Sie verwenden, verhält sich möglicherweise anders als erwartet. (Nachdem Sie den API-Aufruf im Debugger untersucht haben, müssen Sie zur Behebung des Problems möglicherweise die entsprechende Dokumentation zum Ermitteln der richtigen API durchlesen.)
Verwenden Sie die API richtig? Möglicherweise verwenden Sie die richtige API, aber Sie verwenden sie falsch.
Enthält der Code Tippfehler? Manche Rechtschreibfehler, z.B. im Namen einer Variable, sind schwer zu finden, insbesondere dann, wenn Sie mit Sprachen arbeiten, in denen die Variablen vor der Verwendung nicht deklariert werden müssen.
Haben Sie eine Änderung an Ihrem Code vorgenommen, bei der Sie davon ausgehen, dass Sie nichts mit dem aktuellen Problem zu tun hat?
Enthält ein Objekt oder eine Variable einen anderen Wert (oder Werttyp) als erwartet?
Kennen Sie den Zweck des Codes? Es ist schwieriger, Code zu debuggen, den eine andere Person geschrieben hat. Wenn Sie den Code nicht geschrieben haben, müssen Sie nachvollziehen, welchen Zweck der Code hat, bevor Sie diesen effektiv debuggen können.
Tipp
Beim Programmieren sollten Sie mit Code beginnen, der nicht zu umfangreich ist und funktioniert. (Guter Beispielcode ist in diesem Fall hilfreich.) Es ist manchmal einfacher, Probleme in umfangreichem oder kompliziertem Code zu finden, indem Sie mit einem kleinen Codeabschnitt beginnen, der die Kernaufgabe veranschaulicht, die erfüllt werden soll. Anschließend können Sie inkrementell Code ändern oder hinzufügen und den Code an jeder Stelle auf Fehler testen.
Sie können Probleme in Ihrem Code schneller finden, indem Sie Ihre Annahmen in Frage stellen. Möglicherweise können Sie dadurch auch Probleme schneller beheben.
Schrittweises Durchlaufen des Codes im Debugmodus zum Ermitteln der Stelle, an der das Problem aufgetreten ist
Wenn Sie eine App ausführen, sehen Sie Fehler und falsche Ergebnisse normalerweise erst, nachdem der Code ausgeführt wurde. Zudem kann ein Programm unerwartet beendet werden, ohne dass Sie den Grund dafür erfahren.
Das Ausführen einer App in einem Debugger (auch als Debugmodus bezeichnet) bedeutet, dass der Debugger alle Vorgänge während der Ausführung des Programms überwacht. Sie können die App zudem jederzeit anhalten, um ihren Zustand zu überprüfen, und den Code anschließend schrittweise durchlaufen, um jedes Detail in Echtzeit zu beobachten.
In Visual Studio starten Sie den Debugmodus, indem Sie F5 drücken, den Menübefehl Debuggen>Debuggen starten oder die Schaltfläche Debuggen starten () in der Debugsymbolleiste verwenden. Wenn Ausnahmen auftreten, führt die Ausnahmen-Hilfe von Visual Studio Sie zu der Stelle, an der die Ausnahme aufgetreten ist und zeigt weitere hilfreiche Informationen an. Weitere Informationen zum Behandeln von Ausnahmen in Ihrem Code finden Sie unter Debugging techniques and tools (Debugverfahren und Tools).
Wenn keine Ausnahme ausgelöst wurde, haben Sie möglicherweise dennoch eine Idee, an welcher Stelle im Code Sie nach dem Problem suchen müssen. Hierfür verwenden Sie Breakpoints mit dem Debugger, damit Sie den Code gründlicher untersuchen können. Haltepunkte sind eine einfache und wichtige Funktion zum zuverlässigen Debuggen. Ein Breakpoint gibt an, wo Visual Studio im ausgeführten Code angehalten werden soll. So können Sie die Werte von Variablen, das Verhalten des Arbeitsspeichers oder die Sequenz, in der der Code ausgeführt wird, überprüfen.
In Visual Studio können Sie einen Breakpoint schnell festlegen, indem Sie neben einer Codezeile auf den linken Rand klicken. Alternativ können Sie auf eine Zeile zeigen und F9 drücken.
Zur Veranschaulichung dieser Konzepte werden Sie durch Beispielcode geführt, der bereits Fehler enthält. Hier wird C# verwendet, die Features des Debuggers gelten jedoch auch für Visual Basic, C++, JavaScript, Python und andere unterstützte Sprachen. Beispielcode für Visual Basic wird ebenfalls bereitgestellt, die Screenshots liegen jedoch in C# vor.
Erstellen einer Beispiel-App (mit Fehlern)
Nun erstellen Sie eine Anwendung, die einige Fehler enthält.
Visual Studio und die Workload .NET-Desktopentwicklung müssen installiert sein.
Wenn Sie Visual Studio noch nicht installiert haben, können Sie es auf der Seite Visual Studio-Downloads kostenlos herunterladen.
Wenn Sie die Workload installieren müssen und Visual Studio bereits installiert ist, wählen Sie Extras>Tools und Features abrufen.... Der Visual Studio-Installer wird gestartet. Wählen Sie beispielsweise die Workload .NET-Desktopentwicklung aus, und klicken Sie anschließend auf Ändern.
Öffnen Sie Visual Studio.
Wählen Sie im Startfenster Neues Projekt erstellen aus. Geben Sie in das Suchfeld Konsole ein. Wählen Sie als Sprache entweder C# oder Visual Basic und dann die Vorlage Konsolen-App für .NET aus. Wählen Sie Weiter aus. Geben Sie einen Projektnamen wie ConsoleApp_FirstApp ein, und wählen Sie dann Weiter.
Wählen Sie entweder das empfohlene Zielframework oder .NET 8 aus, und wählen Sie Erstellen aus.
Wenn die .NET-Projektvorlage Konsolen-App nicht angezeigt wird, navigieren Sie zu Tools>Tools und Features abrufen, woraufhin der Visual Studio-Installer geöffnet wird. Wählen Sie beispielsweise die Workload .NET-Desktopentwicklung aus, und klicken Sie anschließend auf Ändern.
Visual Studio erstellt das Konsolenprojekt, das im Projektmappen-Explorer (rechter Bereich) angezeigt wird.
Ersetzen Sie in Program.cs (oder Program.vb) den gesamten Standardcode durch den folgenden Code. (Wählen Sie zuerst die richtige Sprachregisterkarte aus, entweder „C#“ oder „Visual Basic“.)
using System; using System.Collections.Generic; namespace ConsoleApp_FirstApp { class Program { static void Main(string[] args) { Console.WriteLine("Welcome to Galaxy News!"); IterateThroughList(); Console.ReadKey(); } private static void IterateThroughList() { var theGalaxies = new List<Galaxy> { new Galaxy() { Name="Tadpole", MegaLightYears=400, GalaxyType=new GType('S')}, new Galaxy() { Name="Pinwheel", MegaLightYears=25, GalaxyType=new GType('S')}, new Galaxy() { Name="Cartwheel", MegaLightYears=500, GalaxyType=new GType('L')}, new Galaxy() { Name="Small Magellanic Cloud", MegaLightYears=.2, GalaxyType=new GType('I')}, new Galaxy() { Name="Andromeda", MegaLightYears=3, GalaxyType=new GType('S')}, new Galaxy() { Name="Maffei 1", MegaLightYears=11, GalaxyType=new GType('E')} }; foreach (Galaxy theGalaxy in theGalaxies) { Console.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears + ", " + theGalaxy.GalaxyType); } // Expected Output: // Tadpole 400, Spiral // Pinwheel 25, Spiral // Cartwheel, 500, Lenticular // Small Magellanic Cloud .2, Irregular // Andromeda 3, Spiral // Maffei 1, 11, Elliptical } } public class Galaxy { public string Name { get; set; } public double MegaLightYears { get; set; } public object GalaxyType { get; set; } } public class GType { public GType(char type) { switch(type) { case 'S': MyGType = Type.Spiral; break; case 'E': MyGType = Type.Elliptical; break; case 'l': MyGType = Type.Irregular; break; case 'L': MyGType = Type.Lenticular; break; default: break; } } public object MyGType { get; set; } private enum Type { Spiral, Elliptical, Irregular, Lenticular} } }
Mit diesem Code sollen der Name der Galaxie, die Entfernung zur Galaxie und der Galaxietyp in einer Liste angezeigt werden. Beim Debuggen ist es wichtig, den Zweck des Codes nachvollziehen zu können. Hier finden Sie das Format für eine Zeile aus der Liste, die in der Ausgabe angezeigt werden soll:
Name der Galaxie, Entfernung, Galaxietyp.
Ausführen der App
Drücken Sie die Taste F5, oder klicken Sie in der Symbolleiste „Debuggen“ (über dem Code-Editor) auf die Schaltfläche Debuggen starten.
Die App wird gestartet, und der Debugger zeigt keine Ausnahmen an. Die Ausgabe im Konsolenfenster entspricht jedoch nicht Ihren Erwartungen. Folgende Ausgabe wird erwartet:
Tadpole 400, Spiral
Pinwheel 25, Spiral
Cartwheel, 500, Lenticular
Small Magellanic Cloud .2, Irregular
Andromeda 3, Spiral
Maffei 1, Elliptical
Stattdessen wird diese Ausgabe angezeigt:
Tadpole 400, ConsoleApp_FirstApp.GType
Pinwheel 25, ConsoleApp_FirstApp.GType
Cartwheel, 500, ConsoleApp_FirstApp.GType
Small Magellanic Cloud .2, ConsoleApp_FirstApp.GType
Andromeda 3, ConsoleApp_FirstApp.GType
Maffei 1, 11, ConsoleApp_FirstApp.GType
Wenn Sie sich die Ausgabe und den Code ansehen, wissen Sie, dass GType
dem Namen der Klasse entspricht, in der der Galaxietyp gespeichert wird. Versuchen Sie nun, tatsächlich den Galaxietyp (z. B. „Spiral“) anzuzeigen, nicht den Namen der Klasse.
Debuggen der App
Fügen Sie einen Breakpoint ein, während die App noch ausgeführt wird.
Klicken Sie mit der rechten Maustaste neben die Methode
Console.WriteLine
, um das Kontextmenü aufzurufen, und wählen Sie im Flyoutmenü Haltepunkt>Haltepunkt einfügen aus.foreach (Galaxy theGalaxy in theGalaxies) { Console.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears + ", " + theGalaxy.GalaxyType); }
Wenn Sie einen Breakpoint festlegen, wird ein roter Punkt am linken Rand angezeigt.
Da ein Problem in der Ausgabe erkennbar ist, sollten Sie mit dem Debuggen im vorangehenden Code beginnen, der die Ausgabe im Debugger festlegt.
Wählen Sie auf der Symbolleiste „Debuggen“ die Schaltfläche Neu starten (STRGUMSCHALTF5 + UMSCHALT + F5).
Die App hält an dem von Ihnen festgelegten Breakpoint an. Die gelbe Markierung gibt an, wo der Debugger angehalten wird (die gelbe Codezeile wurde noch nicht ausgeführt).
Zeigen Sie auf die
GalaxyType
-Variable auf der rechten Seite, und erweitern SietheGalaxy.GalaxyType
auf der linken Seite des Schraubenschlüsselsymbols. Sie können sehen, dassGalaxyType
die EigenschaftMyGType
enthält und der Eigenschaftswert aufSpiral
festgelegt ist.Bei „Spiral“ handelt es sich um den Wert, der eigentlich in der Konsole hätte ausgegeben werden sollen. Es ist also ein guter Anfang, dass Sie während der Ausführung der App auf diesen Wert im Code zugreifen können. In diesem Szenario verwenden Sie die falsche API. Testen Sie nun, ob dieses Problem behoben werden kann, während der Code im Debugger ausgeführt wird.
Zeigen Sie in diesem Code während des Debuggens auf das Ende von
theGalaxy.GalaxyType
, und ändern Sie den Wert intheGalaxy.GalaxyType.MyGType
. Sie können diese Änderung zwar vornehmen, aber der Code-Editor zeigt einen Fehler an, der besagt, dass der Code nicht kompiliert werden kann. (In Visual Basic wird der Fehler nicht angezeigt, und dieser Codeabschnitt funktioniert.)Drücken Sie F11 (Debuggen>Einzelschritt oder auf die Schaltfläche Einzelschritt in der Debugsymbolleiste), um die aktuelle Codezeile auszuführen.
Durch F11 durchläuft der Debugger eine Anweisung nach der anderen (und führt deren Code aus). Bei F10 (Prozedurschritt) handelt es sich um einen ähnlichen Befehl, und beide Befehle sind nützlich, wenn Sie lernen, wie der Debugger verwendet wird.
Das Dialogfeld „Bearbeiten und Fortfahren“ wird angezeigt, das angibt, dass Bearbeitungen nicht kompiliert werden können.
Hinweis
Zum Debuggen des Visual Basic-Beispielcodes überspringen Sie die nächsten Schritte, bis Sie aufgefordert werden, auf die Schaltfläche Neu starten zu klicken.
Wählen Sie im Meldungsfeld Bearbeiten und FortfahrenBearbeiten aus. Sie sehen nun eine Fehlermeldung im Fenster Fehlerliste. Der Fehler gibt an, dass
'object'
keine Definition fürMyGType
enthält.Obwohl Sie jede Galaxie mit einem Objekt vom Typ
GType
(mit der EigenschaftMyGType
) festgelegt haben, erkennt der Debugger den Typ destheGalaxy
-Objekts nicht alsGType
. Woran liegt das? Sehen Sie sich den Code an, mit dem der Galaxietyp festgelegt wird. Dabei werden Sie feststellen, dass dieGType
-Klasse eineMyGType
-Eigenschaft enthält, aber dennoch stimmt etwas nicht. Die Lösung liegt in der Fehlermeldung zuobject
: Der Interpreter für die Sprache hält den Typ des Objekts fürobject
anstatt fürGType
.Wenn Sie den Code betrachten, der für das Festlegen des Galaxietyps verwendet wird, werden Sie feststellen, dass die
GalaxyType
-Eigenschaft derGalaxy
-Klasse aufobject
stattGType
festgelegt ist.public object GalaxyType { get; set; }
Ändern Sie den vorherigen Code wie folgt:
public GType GalaxyType { get; set; }
Wählen Sie in der Symbolleiste „Debuggen“ Neu starten (STRGUMSCHALT + UMSCHALT + F5), um den Code erneut zu kompilieren und neu zu starten.
Wenn der Debugger nun bei
Console.WriteLine
anhält, können Sie auftheGalaxy.GalaxyType.MyGType
zeigen und werden feststellen, dass der Wert ordnungsgemäß festgelegt wurde.Entfernen Sie den Breakpoint, indem Sie auf den roten Punkt am linken Rand klicken (oder mit der rechten Maustaste darauf klicken und Breakpoint>Delete Breakpoint (Breakpoint > Breakpoint entfernen) auswählen), und drücken Sie dann F5, um den Vorgang fortzusetzen.
Die App wird ausgeführt, und eine Ausgabe wird angezeigt. Das Ergebnis sieht jetzt ziemlich gut aus, aber eine Sache fällt Ihnen auf. Sie haben erwartet, dass die Galaxie „Kleine Magellansche Wolke“ in der Konsolenausgabe als irreguläre Galaxie erscheint, aber es wird überhaupt kein Galaxientyp angezeigt.
Tadpole 400, Spiral Pinwheel 25, Spiral Cartwheel, 500, Lenticular Small Magellanic Cloud .2, Andromeda 3, Spiral Maffei 1, Elliptical
Legen Sie in dieser Codezeile einen Breakpoint vor der
switch
-Anweisung (vor derSelect
-Anweisung in Visual Basic) fest.public GType(char type)
In diesem Code wird der Galaxietyp festgelegt, deshalb sollten Sie sich diesen genauer ansehen.
Wählen Sie auf der Symbolleiste „Debuggen“ die Schaltfläche Neu starten (STRGUMSCHALTF5 + UMSCHALT + F5), um einen Neustart durchzuführen.
Der Debugger hält in der Codezeile an, in der Sie den Breakpoint festgelegt haben.
Zeigen Sie auf die Variable
type
. Sie sehen, dass der Wert (dem Zeichencode nach)S
ist. Für Sie ist jedoch der WertI
relevant, da Sie wissen, dass der Galaxietyp „Irregular“ lautet.Drücken Sie F5, und zeigen Sie erneut auf die Variable
type
. Wiederholen Sie diesen Schritt, bis der WertI
in der Variabletype
angezeigt wird.Drücken Sie F11 (Debuggen>Einzelschritt).
Drücken Sie F11, bis Sie bei der Codezeile in der
switch
-Anweisung angelangt sind, die den Wert „I“ enthalten sollte (Select
-Anweisung für Visual Basic). Hier sehen Sie, dass das Problem durch einen Tippfehler erzeugt wird. Sie erwarten, dass der Code fortgesetzt wird, bisMyGType
als „Irregular“ festgelegt wird. Stattdessen überspringt der Debugger diesen Code vollständig und hält im Abschnittdefault
derswitch
-Anweisung an (Else
-Anweisung in Visual Basic).Wenn Sie den Code betrachten, sehen Sie, dass in der
case 'l'
-Anweisung ein Tippfehler ist. Dort solltecase 'I'
stehen.Wählen Sie den Code für
case 'l'
, und ersetzen Sie diesen durchcase 'I'
.Entfernen Sie den Breakpoint, und klicken Sie auf die Schaltfläche Neu starten, um die App neu zu starten.
Die Fehler wurden nun behoben, und die erwartete Ausgabe wird angezeigt!
Drücken Sie eine beliebige Taste, um die App zu beenden.
Zusammenfassung
Wenn ein Problem auftritt, sollten Sie den Debugger und Schrittbefehle wie F10 und F11 verwenden, um den Bereich im Code zu finden, der das Problem auslöst.
Hinweis
Wenn der Bereich mit dem Fehler schwer zu finden ist, legen Sie einen Breakpoint in dem Bereich des Codes fest, der ausgeführt wird, bevor das Problem auftritt, und verwenden Sie anschließend Schrittbefehle, bis das Problem auftritt. Sie können zudem Ablaufverfolgungspunkte verwenden, um Nachrichten im Fenster Ausgabe zu protokollieren. Mithilfe von protokollierten Meldungen (und mithilfe der noch nicht protokollierten Meldungen) können Sie den Bereich, in dem das Problem auftritt, häufig eingrenzen. Sie müssen diesen Vorgang hierfür möglicherweise mehrmals wiederholen.
Wenn Sie den Codebereich mit dem Problem ermitteln, verwenden Sie den Debugger, um es zu untersuchen. Untersuchen Sie den problematischen Code, während Sie die App im Debugger ausführen, um die Ursache des Problems zu ermitteln:
Untersuchen Sie die Variablen, und überprüfen Sie, ob diese die Werte enthalten, die Sie enthalten sollten. Wenn Sie einen falschen Wert finden, sollten Sie ermitteln, wo dieser festgelegt wurde. Starten Sie hierfür den Debugger neu, oder überprüfen Sie die Aufrufliste (oder beides).
Überprüfen Sie, ob die Anwendung den Code ausführt, den Sie erwarten. (In der Beispielanwendung haben Sie beispielsweise erwartet, dass der Code für die
switch
-Anweisung den Galaxietyp auf „Irregular“ festlegt, aber die App hat den Code wegen eines Tippfehlers übersprungen.)
Tipp
Verwenden Sie einen Debugger, um Fehler zu finden. Ein Debugger kann Fehler nur dann für Sie finden, wenn er den Zweck Ihres Codes kennt. Ein Tool kann den Zweck jedoch nur kennen, wenn Sie als Entwickler diesen eindeutig ausdrücken. Dies erreichen Sie mit dem Schreiben von Komponententests.
Nächste Schritte
In diesem Artikel haben Sie einige grundlegende Konzepte für das Debuggen kennengelernt. Als Nächstes erfahren Sie mehr über den Debugger.