Condividi tramite


Debug per principianti assoluti

Invariabilmente, il codice che scriviamo come sviluppatori di software non fa sempre quello che ci aspettavamo che facesse. A volte fa qualcosa di completamente diverso! Quando si verifica l'imprevisto, l'attività successiva consiste nel capire perché e, anche se si potrebbe essere tentati di continuare a guardare il codice per ore, è più semplice ed efficiente usare uno strumento di debug o un debugger.

Un debugger, purtroppo, non è qualcosa che può magicamente rivelare tutti i problemi o i "bug" nel codice. Il debugging significa eseguire il codice passo per passo con uno strumento di debug come Visual Studio, per trovare il punto esatto in cui è stato commesso un errore di programmazione. Si comprende quindi quali correzioni è necessario apportare nel codice e negli strumenti di debug spesso consentono di apportare modifiche temporanee in modo da poter continuare a eseguire il programma.

L'uso di un debugger è anche una competenza che richiede tempo e pratica per imparare, ma è in definitiva un'attività fondamentale per ogni sviluppatore software. In questo articolo vengono presentati i principi di base del debug e vengono forniti suggerimenti per iniziare.

Chiarire il problema ponendosi le domande giuste

Consente di chiarire il problema riscontrato prima di provare a risolverlo. Ci aspettiamo che tu abbia già incontrato un problema nel tuo codice, altrimenti non saresti qui a cercare di capire come risolverlo. Quindi, prima di avviare il debug, assicurarsi di aver identificato il problema che si sta tentando di risolvere:

  • Cosa ci si aspetta che il codice faccia?

  • Che cosa è successo invece?

    Se si verifica un errore (eccezione) durante l'esecuzione dell'app, può essere una buona cosa! Un'eccezione è un evento imprevisto rilevato durante l'esecuzione del codice, in genere un errore di qualche tipo. Uno strumento di debug consente di passare alla posizione esatta nel codice in cui si è verificata l'eccezione e può essere utile per analizzare le possibili correzioni.

    Se è successo qualcos'altro, qual è il sintomo del problema? Si sospetta già dove si è verificato questo problema nel codice? Ad esempio, se il codice visualizza testo, ma il testo non è corretto, si sa che i dati non sono corretti o che il codice che imposta il testo visualizzato presenta un qualche tipo di bug. Analizzando il codice passo passo in un debugger, è possibile esaminare ogni modifica alle variabili per individuare esattamente quando e come vengono assegnati valori non corretti.

Esamina le tue supposizioni

Prima di analizzare un bug o un errore, considerare i presupposti che hanno determinato un determinato risultato. I presupposti nascosti o sconosciuti possono ostacolare l'identificazione di un problema, anche quando si sta osservando direttamente la causa del problema in un debugger. Potresti avere un lungo elenco di possibili presupposti! Ecco alcune domande da porsi per sfidare i tuoi presupposti.

  • Si sta usando l'API corretta, ovvero l'oggetto, la funzione, il metodo o la proprietà corretti? Un'API in uso potrebbe non eseguire le operazioni che si ritiene. Dopo aver esaminato la chiamata API nel debugger, la correzione può richiedere un viaggio nella documentazione per identificare l'API corretta.

  • Si usa correttamente un'API? Forse hai usato l'API corretta ma non l'hai usata nel modo giusto.

  • Il codice contiene errori di digitazioni? Alcuni errori di digitazione, ad esempio un semplice errore di ortografia di un nome di variabile, possono essere difficili da vedere, soprattutto quando si usano linguaggi che non richiedono che le variabili vengano dichiarate prima di essere usate.

  • È stata apportata una modifica al codice e si presuppone che non sia correlato al problema visualizzato?

  • Si prevede che un oggetto o una variabile contenga un determinato valore (o un determinato tipo di valore) diverso da quello che è realmente successo?

  • Si conosce la finalità del codice? Spesso è più difficile eseguire il debug del codice di un altro utente. Se il codice non è il tuo, è possibile che sia necessario dedicare tempo a imparare esattamente cosa fa il codice prima di poter eseguirne il debug in modo efficace.

    Mancia

    Quando si scrive codice, iniziare con piccole dimensioni e iniziare con il codice che funziona. (Un buon codice di esempio è utile qui). In alcuni casi, è più facile correggere un set di codice di grandi dimensioni o complicato iniziando con una piccola parte di codice che illustra l'attività principale che si sta tentando di ottenere. È quindi possibile modificare o aggiungere codice in modo incrementale, verificando in ogni punto gli errori.

Interrogando i presupposti, è possibile ridurre il tempo necessario per individuare un problema nel codice. È anche possibile ridurre il tempo necessario per risolvere un problema.

Eseguire il codice in modalità di debug per individuare dove si è verificato il problema

Quando si esegue normalmente un'app, vengono visualizzati errori e risultati non corretti solo dopo l'esecuzione del codice. Un programma potrebbe anche terminare in modo imprevisto senza indicare perché.

Quando si esegue un'app all'interno di un debugger, chiamata anche modalità di debug , il debugger monitora attivamente tutto ciò che accade durante l'esecuzione del programma. Consente anche di sospendere l'app in qualsiasi momento per esaminarne lo stato e quindi eseguire il codice riga per riga per osservare ogni dettaglio man mano che avviene.

In Visual Studio si passa alla modalità di debug usando F5 o il comando di menu Avvia debugDebugAvvia debug o pulsante Avvia debug pulsante icona che mostra il pulsante Avvia debug. nella barra degli strumenti debug. Se si verificano eccezioni, l'helper eccezioni di Visual Studio consente di raggiungere il punto esatto in cui si è verificata l'eccezione e fornisce altre informazioni utili. Per altre informazioni su come gestire le eccezioni nel codice, vedere Tecniche e strumenti di debug.

Se non hai ottenuto un'eccezione, probabilmente hai una buona idea di dove cercare il problema nel tuo codice. Questo passaggio consente di usare punti di interruzione con il debugger per offrire all'utente la possibilità di esaminare il codice con maggiore attenzione. I punti di interruzione sono la funzionalità di base e essenziale del debug affidabile. Un punto di interruzione indica dove Visual Studio deve sospendere il codice in esecuzione in modo da poter esaminare i valori delle variabili o il comportamento della memoria, la sequenza in cui viene eseguito il codice.

In Visual Studio è possibile impostare rapidamente un punto di interruzione facendo clic sul margine sinistro accanto a una riga di codice. In alternativa, posizionare il cursore su una riga e premere F9.

Per illustrare questi concetti, verranno illustrati alcuni esempi di codice che contengono già diversi bug. Si usa C#, ma le funzionalità di debug si applicano a Visual Basic, C++, JavaScript, Python e ad altri linguaggi supportati. Viene fornito anche il codice di esempio per Visual Basic, ma gli screenshot si trovano in C#.

Creare un'app di esempio (con alcuni bug)

Successivamente, si crea un'applicazione con alcuni bug.

  1. È necessario che Visual Studio sia installato e che sia installato il carico di lavoro sviluppo di applicazioni desktop .NET .

    Se non hai già installato Visual Studio, vai alla pagina di download di Visual Studio per installarlo gratuitamente.

    Se devi installare il pacchetto di lavoro ma Visual Studio è già disponibile, seleziona Tools>Get Tools and Features. Viene avviato il programma di installazione di Visual Studio. Scegliere il carico di lavoro sviluppo di applicazioni desktop .NET, quindi scegliere Modifica.

  2. Apri Visual Studio.

    Nella finestra iniziale scegliere Crea un nuovo progetto. Digitare console nella casella di ricerca, selezionare C# o Visual Basic come linguaggio e quindi scegliere Console App per .NET. Scegliere Avanti. Digitare ConsoleApp_FirstApp come nome del progetto e selezionare Avanti.

    Se si usa un nome di progetto diverso, sarà necessario modificare il valore dello spazio dei nomi in modo che corrisponda al nome del progetto quando si copia il codice di esempio.

    Puoi scegliere il framework di destinazione consigliato o .NET 8, e poi selezionare Crea.

    Se non vedi il modello di progetto Console App per .NET, vai su Tools>Get Tools and Features, che apre il Visual Studio Installer. Scegliere il carico di lavoro sviluppo di applicazioni desktop .NET, quindi scegliere Modifica.

    Visual Studio crea il progetto console visualizzato in esplora soluzioni nel riquadro destro.

  3. In Program.cs (o Program.vb) sostituire tutto il codice predefinito con il codice seguente. Selezionare prima la scheda del linguaggio corretta, C# o 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}
        }
    }
    

    Il nostro intento per questo codice è quello di visualizzare il nome della galassia, la distanza alla galassia e il tipo di galassia tutto in un elenco. Per eseguire il debug, è importante comprendere la finalità del codice. Ecco il formato per una riga dall'elenco che si vuole visualizzare nell'output:

    nome della galassia, distanza, tipo di galassia.

Eseguire l'app

Premere F5 o il pulsante Avvia debug pulsante icona che mostra il pulsante Avvia debug. nella barra degli strumenti debug, che si trova sopra l'editor di codice.

L'app si avvia e non ci sono eccezioni mostrate dal debugger. Tuttavia, l'output visualizzato nella finestra della console non è quello previsto. Ecco l'output previsto:

Tadpole  400,  Spiral
Pinwheel  25,  Spiral
Cartwheel, 500,  Lenticular
Small Magellanic Cloud .2,  Irregular
Andromeda  3,  Spiral
Maffei 1,  Elliptical

Tuttavia, viene visualizzato questo output:

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

Esaminando l'output e il codice, sappiamo che GType è il nome della classe che archivia il tipo di galassia. Stiamo cercando di mostrare il tipo di galassia effettivo (ad esempio "Spiral"), non il nome della classe!

Eseguire il debug dell'app

  1. Con l'app ancora in esecuzione, inserire un punto di interruzione.

    Nel ciclo foreach, fare clic con il pulsante destro del mouse accanto al metodo Console.WriteLine per aprire il menu contestuale e selezionare Breakpoint>Inserisci Breakpoint dal sottomenu.

    foreach (Galaxy theGalaxy in theGalaxies)
    {
        Console.WriteLine(theGalaxy.Name + "  " + theGalaxy.MegaLightYears + ",  " + theGalaxy.GalaxyType);
    }
    

    Quando si imposta il punto di interruzione, viene visualizzato un punto rosso nel margine sinistro.

    Come si nota un problema nell'output, si avvia il debug esaminando il codice precedente che imposta l'output nel debugger.

  2. Selezionare l'icona Riavviache rappresenta l'operazione RestartApp nella barra degli strumenti di debug. Pulsante nella barra degli strumenti di debug (Ctrl + Shift + F5).

    L'app viene sospesa nel punto di interruzione impostato. L'evidenziazione gialla indica dove viene sospeso il debugger (la riga gialla di codice non è ancora stata eseguita).

  3. Posiziona il cursore sulla variabile GalaxyType sulla destra e quindi, a sinistra dell'icona della chiave, espandi theGalaxy.GalaxyType. Si noterà che GalaxyType contiene una proprietà MyGTypee il valore della proprietà è impostato su Spiral.

    Screenshot del debugger di Visual Studio con una riga di codice in giallo e un menu aperto sotto la proprietà Galaxy GalaxyType.

    "Spiral" è in realtà il valore corretto che ci si aspettava di visualizzare nella console! È quindi un buon punto di partenza che è possibile accedere al valore in questo codice durante l'esecuzione dell'app. In questo scenario si usa l'API non corretta. Vediamo se è possibile risolvere questo problema durante l'esecuzione del codice nel debugger.

  4. Nello stesso codice, durante il debug, posiziona il cursore alla fine di theGalaxy.GalaxyType e modificalo in theGalaxy.GalaxyType.MyGType. Anche se è possibile apportare la modifica, l'editor di codice mostra un errore (riga ondulata rossa). In Visual Basic l'errore non viene visualizzato e questa sezione di codice funziona.

  5. Premere F11 (Debug>Passa dentro o il pulsante Passa dentro nella barra degli strumenti di debug) per eseguire la riga di codice corrente.

    F11 avanza il debugger (e esegue il codice) un'istruzione alla volta. F10 (Step Over) è un comando simile ed entrambi sono utili per imparare a usare il debugger.

    Quando si tenta di avanzare con il debugger, viene visualizzata la finestra di dialogo Ricaricamento a caldo che indica che le modifiche non possono essere compilate.

    Screenshot del debugger di Visual Studio con una riga di codice evidenziata in rosso e una finestra di messaggio con l'opzione Modifica selezionata.

    Viene visualizzata la finestra di dialogo Modifica e continuazione che indica che le modifiche non possono essere compilate.

    Screenshot del debugger di Visual Studio con una riga di codice evidenziata in rosso e una finestra di messaggio con l'opzione Modifica selezionata.

    Nota

    Per eseguire il debug del codice di esempio di Visual Basic, ignorare i passaggi successivi fino a quando non viene richiesto di fare clic sull'icona Riavviache mostra il pulsante Riavvia app sulla barra degli strumenti Debug. pulsante.

  6. Selezionare Modifica nella finestra del messaggio di Hot Reload o di Modifica e Continua . Ora visualizzi un messaggio di errore nella finestra elenco errori. L'errore indica che il 'object' non contiene una definizione per MyGType.

    Screenshot del debugger di Visual Studio con una riga di codice evidenziata in rosso e una finestra Elenco errori con due errori elencati.

    Anche se ogni galassia viene impostata con un oggetto di tipo GType (che ha la proprietà MyGType), il debugger non riconosce l'oggetto theGalaxy come oggetto di tipo GType. Cosa sta succedendo? Si vuole esaminare qualsiasi codice che imposta il tipo di galassia. Quando si esegue questa operazione, si noterà che la classe GType ha sicuramente una proprietà di MyGType, ma qualcosa non è corretto. Il messaggio di errore su object risulta essere l'indizio; all'interprete linguistico, il tipo sembra essere un oggetto di tipo object anziché un oggetto di tipo GType.

  7. Esaminando il codice correlato all'impostazione del tipo di galassia, si trova la proprietà GalaxyType della classe Galaxy viene specificata come object anziché GType.

    public object GalaxyType { get; set; }
    
  8. Modificare il codice precedente come segue:

    public GType GalaxyType { get; set; }
    
  9. Selezionare l'iconaRiavvia che mostra il pulsante Riavvia app nella barra degli strumenti Debug. pulsante nella barra degli strumenti di debug (CTRLMAIUSCF5) per ricompilare il codice e riavviare.

    Ora, quando il debugger viene sospeso in Console.WriteLine, è possibile passare il puntatore del mouse su theGalaxy.GalaxyType.MyGTypee verificare che il valore sia impostato correttamente.

  10. Rimuovere il punto di interruzione facendo clic sul cerchio del punto di interruzione nel margine sinistro oppure facendo clic con il pulsante destro del mouse e scegliendo punto di interruzione>Elimina punto di interruzione), quindi premere F5 per continuare.

    L'app si esegue e visualizza l'output. È bello, ma si nota una cosa. Si prevedeva che la galassia Piccola Nube di Magellano si presentasse come una galassia irregolare nell'output della console, ma non mostra nessun tipo di galassia.

    Tadpole  400,  Spiral
    Pinwheel  25,  Spiral
    Cartwheel, 500,  Lenticular
    Small Magellanic Cloud .2,
    Andromeda  3,  Spiral
    Maffei 1,  Elliptical
    
  11. Impostare un punto di interruzione su questa riga di codice prima dell'istruzione switch (prima dell'istruzione Select in Visual Basic).

    public GType(char type)
    

    Questo codice è dove è impostato il tipo di galassia, quindi vogliamo dare un'occhiata più da vicino.

  12. Selezionare l'icona Riavviache mostra il pulsante Riavvia app nella barra degli strumenti di debug. Selezionare anche il pulsante nella barra degli strumenti di debug (Ctrl + Maiusc + F5) per riavviare.

    Il debugger viene sospeso nella riga di codice in cui si imposta il punto di interruzione.

  13. Passare il puntatore del mouse sulla variabile type. Viene visualizzato un valore di S (seguendo il codice carattere). Sei interessato a un valore di I, dato che sai che è un tipo di galassia irregolare.

  14. Premere F5 e passare di nuovo il puntatore del mouse sulla variabile type. Ripetere questo passaggio finché non viene visualizzato un valore di I nella variabile type.

    Screenshot del debugger di Visual Studio con una riga di codice in giallo e una finestra con il valore della variabile di tipo 73 I.

  15. A questo punto, premere F11 (Debug>Passo nel).

  16. Premere F11 fino a quando non si interrompe la riga di codice nell'istruzione switch per un valore "I" ( istruzioneSelect per Visual Basic). Qui viene visualizzato un problema chiaro risultante da un errore di digitatura. Ci si aspettava che il codice avanzasse al punto in cui imposta MyGType come tipo di galassia irregolare, ma il debugger ignora completamente questo codice e si ferma sulla sezione default dell'istruzione switch (istruzioneElse in Visual Basic).

    Screenshot che mostra l'errore di digitatura.

    Esaminando il codice, viene visualizzato un errore di digitazione nell'istruzione case 'l'. Deve essere case 'I'.

  17. Selezionare il codice case 'l' e sostituirlo con case 'I'.

  18. Rimuovere il punto di interruzione e quindi selezionare il pulsante Riavvia per riavviare l'app.

    I bug sono stati corretti ora e viene visualizzato l'output previsto.

    Premere un tasto qualsiasi per completare l'app.

Sommario

Quando viene visualizzato un problema, usare il debugger e i comandi dei passaggi , ad esempio F10 e F11 per trovare l'area del codice con il problema.

Nota

Se è difficile identificare l'area del codice in cui si verifica il problema, impostare un punto di interruzione nel codice eseguito prima che si verifichi il problema e quindi usare i comandi di passaggio fino a visualizzare il manifesto del problema. È anche possibile usare punti di traccia per registrare i messaggi nella finestra output . Esaminando i messaggi registrati e notando quali messaggi non sono ancora stati registrati, è spesso possibile isolare l'area del codice con il problema. Potrebbe essere necessario ripetere questo processo più volte per restringerlo.

Quando si trova l'area del codice con il problema, usare il debugger per analizzare. Per individuare la causa di un problema, esaminare il codice del problema durante l'esecuzione dell'app nel debugger:

  • Controllare le variabili e verificare se contengono il tipo di valori che devono contenere. Se si trova un valore non valido, scoprire dove è stato impostato il valore non valido (per trovare dove è stato impostato il valore, potrebbe essere necessario riavviare il debugger, esaminare lo stack di chiamate o entrambi).

  • Controllare se l'applicazione sta eseguendo il codice previsto. Nell'applicazione di esempio, ci aspettavamo che il codice per l'istruzione switch impostasse il tipo di galassia su Irregolare, ma l'app ha saltato il codice a causa dell'errore di digitazione.

Mancia

Si usa un debugger per individuare i bug. Uno strumento di debug può trovare bug per te solo se conosce l'intento del tuo codice. Uno strumento può conoscere la finalità del codice solo se lo sviluppatore esprime tale finalità. Scrivere unit test è il modo di farlo.

Passaggi successivi

In questo articolo sono stati appresi alcuni concetti generali relativi al debug. Successivamente, è possibile iniziare a ottenere altre informazioni sul debugger.