Debugowanie dla bezwzględnych początkujących

Bez awarii kod, który piszemy jako deweloperzy oprogramowania, nie zawsze robi to, czego oczekiwaliśmy. Czasami robi to coś zupełnie innego! Gdy wystąpi nieoczekiwana sytuacja, następnym zadaniem jest ustalenie, dlaczego i chociaż możemy być kuszeni, aby po prostu nadal wpatrować się w nasz kod przez wiele godzin, łatwiej i wydajniej używać narzędzia debugowania lub debugera.

Debuger, niestety, nie jest czymś, co może magicznie ujawnić wszystkie problemy lub "błędy" w naszym kodzie. Debugowanie oznacza uruchamianie kodu krok po kroku w narzędziu debugowania, takiego jak Visual Studio, w celu znalezienia dokładnego punktu, w którym wystąpił błąd programowania. Następnie rozumiesz, jakie poprawki należy wprowadzić w kodzie i narzędziach do debugowania, często umożliwiają wprowadzanie tymczasowych zmian, dzięki czemu można kontynuować uruchamianie programu.

Efektywne korzystanie z debugera jest również umiejętnością, która zajmuje dużo czasu i praktykowania, ale ostatecznie jest podstawowym zadaniem dla każdego dewelopera oprogramowania. W tym artykule wprowadzimy podstawowe zasady debugowania i przedstawimy wskazówki, które pomogą Ci rozpocząć pracę.

Wyjaśnij problem, zadając sobie odpowiednie pytania

Pomaga to wyjaśnić problem napotkany przed podjęciem próby jego rozwiązania. Oczekujemy, że wystąpił już problem w kodzie. W przeciwnym razie nie będziesz tutaj próbował dowiedzieć się, jak go debugować! Przed rozpoczęciem debugowania upewnij się, że zidentyfikowano problem, który próbujesz rozwiązać:

  • Czego oczekiwaliśmy od kodu?

  • Co się stało zamiast tego?

    Jeśli wystąpi błąd (wyjątek) podczas uruchamiania aplikacji, może to być dobra rzecz! Wyjątek jest nieoczekiwanym zdarzeniem napotkanym podczas uruchamiania kodu, zazwyczaj błędem pewnego rodzaju. Narzędzie do debugowania może przejść do dokładnego miejsca w kodzie, w którym wystąpił wyjątek, i może pomóc w zbadaniu możliwych poprawek.

    Jeśli coś innego się stało, jaki jest objaw problemu? Czy już podejrzewasz, gdzie wystąpił ten problem w kodzie? Jeśli na przykład kod wyświetla jakiś tekst, ale tekst jest niepoprawny, wiesz, że dane są nieprawidłowe lub kod, który ustawił tekst wyświetlany, ma jakąś usterkę. Wykonując kroki kodu w debugerze, możesz zbadać każdą i każdą zmianę zmiennych, aby dowiedzieć się dokładnie, kiedy i jak są przypisywane nieprawidłowe wartości.

Sprawdzanie założeń

Zanim zbadasz usterkę lub błąd, pomyśl o założeniach, które sprawiły, że oczekiwano określonego wyniku. Ukryte lub nieznane założenia mogą stać się sposobem identyfikowania problemu, nawet jeśli patrzysz bezpośrednio na przyczynę problemu w debugerze. Może istnieć długa lista możliwych założeń! Oto kilka pytań, które należy zadać sobie w celu zakwestionowania założeń.

  • Czy używasz odpowiedniego interfejsu API (czyli odpowiedniego obiektu, funkcji, metody lub właściwości)? Używany interfejs API może nie robić tego, co myślisz. (Po przeanalizowaniu wywołania interfejsu API w debugerze naprawa może wymagać podróży do dokumentacji, aby ułatwić zidentyfikowanie poprawnego interfejsu API).

  • Czy używasz interfejsu API poprawnie? Być może użyto odpowiedniego interfejsu API, ale nie użyto go w odpowiedni sposób.

  • Czy kod zawiera jakieś literówki? Niektóre literówki, takie jak proste błędy pisowni nazwy zmiennej, mogą być trudne do wyświetlenia, zwłaszcza podczas pracy z językami, które nie wymagają zadeklarowania zmiennych przed ich użyciem.

  • Czy wprowadziliśmy zmianę w kodzie i przyjęto założenie, że nie ma to związku z widocznym problemem?

  • Czy oczekiwano, że obiekt lub zmienna będzie zawierać określoną wartość (lub określony typ wartości), która różni się od tego, co naprawdę się stało?

  • Czy znasz intencję kodu? Często trudniej jest debugować kod innej osoby. Jeśli nie jest to twój kod, może być konieczne poświęcanie czasu na naukę dokładnie tego, co robi kod, zanim będzie można go skutecznie debugować.

    Napiwek

    Podczas pisania kodu zacznij od małego i zacznij od kodu, który działa! (Dobry przykładowy kod jest tutaj przydatny). Czasami łatwiej jest naprawić duży lub skomplikowany zestaw kodu, zaczynając od małego fragmentu kodu, który demonstruje podstawowe zadanie, które próbujesz osiągnąć. Następnie można modyfikować lub dodawać kod przyrostowo, testując w każdym momencie pod kątem błędów.

Kwestiując założenia, możesz skrócić czas znajdowania problemu w kodzie. Możesz również skrócić czas potrzebny na rozwiązanie problemu.

Przejdź przez kod w trybie debugowania, aby dowiedzieć się, gdzie wystąpił problem

Podczas normalnego uruchamiania aplikacji są wyświetlane błędy i nieprawidłowe wyniki dopiero po uruchomieniu kodu. Program może również zakończyć się nieoczekiwanie bez informowania o tym, dlaczego.

Po uruchomieniu aplikacji w debugerze, nazywanym również trybem debugowania, debuger aktywnie monitoruje wszystko, co dzieje się podczas uruchamiania programu. Umożliwia również wstrzymanie aplikacji w dowolnym momencie w celu zbadania jej stanu, a następnie przejście przez wiersz kodu w celu obserwowania wszystkich szczegółów w ten sposób.

W programie Visual Studio wprowadzasz tryb debugowania przy użyciu klawisza F5 (lub polecenia menu Debuguj>rozpocznij debugowanie lub przyciskuIcon showing Start Debugging button.Rozpocznij debugowanie na pasku narzędzi debugowania). Jeśli wystąpią jakiekolwiek wyjątki, pomocnik wyjątków programu Visual Studio przeniesie Cię do dokładnego punktu, w którym wystąpił wyjątek i udostępnia inne przydatne informacje. Aby uzyskać więcej informacji na temat obsługi wyjątków w kodzie, zobacz Debugowanie technik i narzędzi.

Jeśli nie otrzymasz wyjątku, prawdopodobnie masz dobry pomysł, gdzie szukać problemu w kodzie. W tym kroku używasz punktów przerwania z debugerem, aby dać sobie szansę na dokładniejsze zbadanie kodu. Punkty przerwania to najbardziej podstawowa i kluczowa funkcja wiarygodnego debugowania. Punkt przerwania wskazuje, gdzie program Visual Studio powinien wstrzymać uruchomiony kod, aby przyjrzeć się wartościom zmiennych lub zachowaniu pamięci, sekwencji uruchamiania kodu.

W programie Visual Studio można szybko ustawić punkt przerwania, klikając lewy margines obok wiersza kodu. Możesz też umieścić kursor w wierszu i nacisnąć klawisz F9.

Aby ułatwić zilustrowanie tych pojęć, przeprowadzimy Cię przez przykładowy kod, który zawiera już kilka usterek. Używamy języka C#, ale funkcje debugowania mają zastosowanie do języków Visual Basic, C++, JavaScript, Python i innych obsługiwanych języków. Podano również przykładowy kod dla języka Visual Basic, ale zrzuty ekranu znajdują się w języku C#.

Tworzenie przykładowej aplikacji (z niektórymi usterkami)

Następnie utworzysz aplikację, która zawiera kilka usterek.

  1. Musisz mieć zainstalowany program Visual Studio i zainstalowany pakiet roboczy programowanie aplikacji klasycznych platformy .NET.

    Jeśli program Visual Studio nie został jeszcze zainstalowany, przejdź do strony pobierania programu Visual Studio, aby zainstalować ją bezpłatnie.

    Jeśli musisz zainstalować obciążenie, ale masz już program Visual Studio, wybierz pozycję Narzędzia>Pobierz narzędzia i funkcje. Zostanie uruchomiona Instalator programu Visual Studio. Wybierz obciążenie programowanie aplikacji klasycznych platformy .NET, a następnie wybierz pozycję Modyfikuj.

  2. Otwórz program Visual Studio.

    W oknie uruchamiania wybierz pozycję Utwórz nowy projekt. Wpisz konsolę w polu wyszukiwania, wybierz język C# lub Visual Basic jako język, a następnie wybierz pozycję Aplikacja konsolowa dla platformy .NET. Wybierz Dalej. Wpisz nazwę projektu, taką jak ConsoleApp_FirstApp , a następnie wybierz przycisk Dalej.

    Wybierz zalecaną strukturę docelową lub platformę .NET 8, a następnie wybierz pozycję Utwórz.

    Jeśli nie widzisz szablonu projektu Aplikacja konsolowa dla platformy .NET, przejdź do pozycji Narzędzia>Pobierz narzędzia i funkcje, co spowoduje otwarcie Instalator programu Visual Studio. Wybierz obciążenie programowanie aplikacji klasycznych platformy .NET, a następnie wybierz pozycję Modyfikuj.

    Program Visual Studio tworzy projekt konsoli, który jest wyświetlany w Eksplorator rozwiązań w okienku po prawej stronie.

  3. W pliku Program.cs (lub Program.vb) zastąp cały kod domyślny poniższym kodem. (Najpierw wybierz odpowiednią kartę języka— C# lub 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}
        }
    }
    

    Naszym zamiarem dla tego kodu jest wyświetlenie nazwy galaktyki, odległości do galaktyki i typu galaktyki na liście. Aby debugować, ważne jest zrozumienie intencji kodu. Oto format jednego wiersza z listy, który chcemy pokazać w danych wyjściowych:

    nazwa galaktyki, odległość, typ galaktyki.

Uruchom aplikację

Naciśnij klawisz F5 lub przycisk Icon showing Start Debugging button.Rozpocznij debugowanie na pasku narzędzi debugowania znajdującym się nad edytorem kodu.

Aplikacja jest uruchamiana i nie ma żadnych wyjątków wyświetlanych przez debuger. Jednak dane wyjściowe widoczne w oknie konsoli nie są oczekiwane. Oto oczekiwane dane wyjściowe:

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

Zamiast tego zobaczysz następujące dane wyjściowe:

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

Patrząc na dane wyjściowe i nasz kod, wiemy, że GType jest to nazwa klasy, która przechowuje typ galaktyki. Staramy się pokazać rzeczywisty typ galaktyki (taki jak "Spiral"), a nie nazwę klasy!

Debugowanie aplikacji

  1. Gdy aplikacja nadal działa, wstaw punkt przerwania.

    Kliknij prawym przyciskiem myszy obok Console.WriteLine metody , aby pobrać menu kontekstowe, a następnie wybierz pozycję Punkt przerwania Wstaw punkt przerwania>z menu wysuwanego.

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

    Po ustawieniu punktu przerwania czerwona kropka pojawi się na lewym marginesie.

    Jak widzisz problem w danych wyjściowych, rozpoczniesz debugowanie, patrząc na powyższy kod, który ustawia dane wyjściowe w debugerze.

  2. Wybierz przycisk Uruchom ponownieIcon showing RestartApp button in Debug toolbar. na pasku narzędzi debugowania (Ctrl Shift + + F5).

    Aplikacja wstrzymuje się w ustawionym punkcie przerwania. Żółte wyróżnienie wskazuje, gdzie debuger jest wstrzymany (żółty wiersz kodu nie został jeszcze wykonany).

  3. Umieść kursor na zmiennej GalaxyType po prawej stronie, a następnie po lewej stronie ikony klucza rozwiń pozycję theGalaxy.GalaxyType. Zobaczysz, że GalaxyType zawiera właściwość MyGType, a wartość właściwości jest ustawiona na Spiralwartość .

    Screenshot of the Visual Studio Debugger with a line of code in yellow and a menu open below the Galaxy GalaxyType property.

    "Spiral" jest rzeczywiście prawidłową wartością, której oczekiwano wydrukowania w konsoli! Dlatego dobrym początkiem jest uzyskanie dostępu do wartości w tym kodzie podczas uruchamiania aplikacji. W tym scenariuszu używamy niepoprawnego interfejsu API. Sprawdźmy, czy możesz rozwiązać ten problem podczas uruchamiania kodu w debugerze.

  4. W tym samym kodzie, podczas debugowania, umieść kursor na końcu theGalaxy.GalaxyType i zmień go na theGalaxy.GalaxyType.MyGType. Mimo że można wprowadzić zmianę, edytor kodu wyświetla błąd wskazujący, że nie może skompilować tego kodu. (W Visual Basic błąd nie jest wyświetlany, a ta sekcja kodu działa).

  5. Naciśnij klawisz F11 (Debuguj krok do lub Przejdź do) na pasku narzędzi debugowania>, aby wykonać bieżący wiersz kodu.

    F11 przechodzi debuger (i wykonuje kod) jedną instrukcję naraz. Klawisz F10 (Step Over) jest podobnym poleceniem i oba są przydatne podczas uczenia się, jak używać debugera.

    Zostanie wyświetlone okno dialogowe Edytowanie i kontynuowanie wskazujące, że nie można skompilować edycji.

    Screenshot of the Visual Studio Debugger with a line of code highlighted in red and a message box with the Edit option selected.

    Uwaga

    Aby debugować przykładowy kod języka Visual Basic, pomiń kilka następnych kroków, dopóki nie zostanie wyświetlony monit o kliknięcie przycisku Uruchom ponownieIcon showing Restart app button in Debug toolbar..

  6. Wybierz pozycję Edytuj w oknie komunikatu Edytuj i kontynuuj. W oknie Lista błędów zostanie wyświetlony komunikat o błędzie. Błąd wskazuje, że element 'object' nie zawiera definicji elementu MyGType.

    Screenshot of the Visual Studio Debugger with a line of code highlighted in red and an Error List window with two errors listed.

    Mimo że ustawiamy każdą galaktykę z obiektem typu GType (który ma MyGType właściwość), debuger nie rozpoznaje theGalaxy obiektu jako obiektu typu GType. Co się dzieje? Chcesz przejrzeć dowolny kod, który ustawia typ galaktyki. Gdy to zrobisz, zobaczysz, że GType klasa na pewno ma właściwość MyGType, ale coś nie jest w porządku. Komunikat object o błędzie dotyczący okazuje się być wskazówką. Dla interpretera języka typ wydaje się być obiektem typu object zamiast obiektu typu GType.

  7. Przeglądając kod związany z ustawieniem typu galaktyki, można znaleźć GalaxyType właściwość Galaxy klasy jest określona jako object zamiast GType.

    public object GalaxyType { get; set; }
    
  8. Zmień poprzedni kod w następujący sposób:

    public GType GalaxyType { get; set; }
    
  9. Wybierz przycisk Uruchom ponownieIcon showing Restart app button in Debug toolbar. na pasku narzędzi debugowania (Ctrl Shift + + F5), aby ponownie skompilować kod i ponownie uruchomić.

    Teraz, gdy debuger wstrzymuje się na Console.WriteLine, możesz zatrzymać wskaźnik myszy na theGalaxy.GalaxyType.MyGType, i zobaczyć, że wartość jest poprawnie ustawiona.

  10. Usuń punkt przerwania, klikając okrąg punktu przerwania na lewym marginesie (lub kliknij prawym przyciskiem myszy i wybierz pozycję Punkt przerwania Usuń punkt> przerwania), a następnie naciśnij klawisz F5, aby kontynuować.

    Aplikacja jest uruchamiana i wyświetla dane wyjściowe. Wygląda dobrze, ale zauważasz jedną rzecz. Spodziewałeś się, że mała galaktyka Magellanic Cloud pojawi się jako nieregularna galaktyka w danych wyjściowych konsoli, ale w ogóle nie pokazuje typu galaktyki.

    Tadpole  400,  Spiral
    Pinwheel  25,  Spiral
    Cartwheel, 500,  Lenticular
    Small Magellanic Cloud .2,
    Andromeda  3,  Spiral
    Maffei 1,  Elliptical
    
  11. Ustaw punkt przerwania w tym wierszu kodu przed instrukcją (przed instrukcją switchSelect w Visual Basic).

    public GType(char type)
    

    Ten kod polega na tym, że typ galaktyki jest ustawiony, więc chcemy przyjrzeć się temu bliżej.

  12. Wybierz przycisk Uruchom ponownieIcon showing Restart app button in Debug toolbar. na pasku narzędzi debugowania (Ctrl Shift + + F5), aby ponownie uruchomić.

    Debuger wstrzymuje się w wierszu kodu, w którym ustawiono punkt przerwania.

  13. Umieść kursor na zmiennej type . Zostanie wyświetlona S wartość (po kodzie znaku). Interesuje Cię wartość I, ponieważ wiesz, że jest to nieregularny typ galaktyki.

  14. Naciśnij klawisz F5 i ponownie umieść kursor nad zmienną type . Powtórz ten krok do momentu wyświetlenia wartości I w zmiennej type .

    Screenshot of the Visual Studio Debugger with a line of code in yellow and a window with the type variable value of 73 I.

  15. Teraz naciśnij klawisz F11 (Debuguj>krok do).

  16. Naciśnij klawisz F11 , dopóki nie zatrzymasz się w wierszu kodu w switch instrukcji dla wartości "I" (Select instrukcja języka Visual Basic). W tym miejscu zobaczysz wyraźny problem wynikający z literówki. Oczekiwano, że kod przechodzi do lokalizacji, w której jest ustawiany MyGType jako typ galaktyki Nieregularny, ale debuger pomija ten kod całkowicie i wstrzymuje się w default sekcji switch instrukcji (Else instrukcja w Visual Basic).

    Screenshot showing the typo error.

    Patrząc na kod, w instrukcji zobaczysz literówkę case 'l' . Powinna ona mieć wartość case 'I'.

  17. Wybierz element w kodzie case 'l' i zastąp go ciągiem case 'I'.

  18. Usuń punkt przerwania, a następnie wybierz przycisk Uruchom ponownie , aby ponownie uruchomić aplikację.

    Usterki zostały naprawione teraz i zostaną wyświetlone oczekiwane dane wyjściowe.

    Naciśnij dowolny klawisz, aby zakończyć aplikację.

Podsumowanie

Gdy wystąpi problem, użyj debugera i poleceń kroków , takich jak F10 i F11 , aby znaleźć region kodu z problemem.

Uwaga

Jeśli trudno jest zidentyfikować region kodu, w którym występuje problem, ustaw punkt przerwania w kodzie uruchamianym przed wystąpieniem problemu, a następnie użyj poleceń kroków do momentu wyświetlenia manifestu problemu. Możesz również użyć punktów śledzenia do rejestrowania komunikatów w oknie Dane wyjściowe . Patrząc na zarejestrowane komunikaty (i zauważając, które komunikaty nie zostały jeszcze zarejestrowane!), często można odizolować region kodu z problemem. Może być konieczne powtórzenie tego procesu kilka razy, aby go zawęzić.

Jeśli znajdziesz region kodu z problemem, użyj debugera do zbadania. Aby znaleźć przyczynę problemu, sprawdź kod problemu podczas uruchamiania aplikacji w debugerze:

  • Sprawdź zmienne i sprawdź, czy zawierają one typ wartości, które powinny zawierać. Jeśli znajdziesz nieprawidłową wartość, sprawdź, gdzie ustawiono nieprawidłową wartość (aby znaleźć, gdzie ustawiono wartość, może być konieczne ponowne uruchomienie debugera, przyjrzenie się stosowi wywołań lub obu).

  • Sprawdź, czy aplikacja wykonuje oczekiwany kod. (Na przykład w przykładowej aplikacji spodziewaliśmy się, że kod instrukcji switch ustawi typ galaktyki na Nieregularny, ale aplikacja pominąła kod ze względu na literówkę).

Napiwek

Debuger ułatwia znajdowanie usterek. Narzędzie do debugowania może znaleźć usterki tylko wtedy, gdy zna intencję kodu. Narzędzie może znać intencję kodu tylko wtedy, gdy deweloper wyrazi ten zamiar. Pisanie testów jednostkowych to sposób, w jaki to robisz.

Następne kroki

W tym artykule przedstawiono kilka ogólnych pojęć dotyczących debugowania. Następnie możesz dowiedzieć się więcej na temat debugera.