Ćwiczenie — debugowanie za pomocą programu Visual Studio Code

Ukończone

Nadszedł czas, aby przećwiczyć nowo zdobytą wiedzę na temat debugowania. Dzisiaj rozpoczynasz pracę nad tym zadaniem — przyszedł czas na zastosowanie umiejętności debugowania platformy .NET w procesie naprawiania usterki w sztandarowym produkcie firmy, kalkulatorze Fibonacciego.

Tworzenie przykładowego projektu platformy .NET na potrzeby debugowania

Aby skonfigurować program Visual Studio Code na potrzeby debugowania platformy .NET, najpierw będzie nam potrzebny projekt platformy .NET. Program Visual Studio Code obejmuje zintegrowany terminal, co ułatwia utworzenie nowego projektu.

  1. W programie Visual Studio Code wybierz pozycję File (Plik)>Open Folder (Otwórz folder).

  2. W wybranej lokalizacji utwórz nowy folder o nazwie DotNetDebugging. Następnie wybierz pozycję Wybierz folder.

  3. Otwórz zintegrowany terminal z programu Visual Studio Code, wybierając pozycję View (Widok)>Terminal z menu głównego.

  4. Skopiuj następujące polecenie i wklej je w oknie terminalu:

    dotnet new console
    

    To polecenie umożliwia utworzenie w Twoim folderze pliku Program.cs z gotowym podstawowym programem „Hello World”. Utworzony również zostanie plik projektu C# o nazwie DotNetDebugging.csproj.

  5. Skopiuj następujące polecenie i wklej je w oknie terminalu, aby uruchomić program „Hello World”.

    dotnet run
    

    W oknie terminalu zostanie wyświetlony komunikat "Hello World!" jako dane wyjściowe.

Konfigurowanie programu Visual Studio Code na potrzeby debugowania platformy .NET

  1. Otwórz plik Program.cs, wybierając go.

  2. Po pierwszym otwarciu pliku z kodem C# w programie Visual Studio Code zostanie wyświetlony monit o zainstalowanie zalecanych rozszerzeń dla języka C#. Wybierz przycisk Instaluj, jeśli zostanie wyświetlony monit.

    Zrzut ekranu przedstawiający monit programu Visual Studio Code o zainstalowanie rozszerzenia języka C#.

  3. Program Visual Studio Code zainstaluje rozszerzenie języka C# i wyświetli kolejny monit o dodanie wymaganych zasobów do kompilowania i debugowania projektu. Wybierz przycisk Tak.

    Zrzut ekranu przedstawiający monit programu Visual Studio Code o dodanie wymaganych zasobów w celu kompilowania i debugowania projektu środowiska .NET.

  4. Możesz zamknąć kartę Rozszerzenie: C# , aby skoncentrować się na kodzie, który będziemy debugować.

Dodawanie logiki programu Fibonacciego

Nasz bieżący projekt wypisuje w konsoli komunikat „Hello World”, co nie dostarcza nam zbyt wielu danych do debugowania. Zamiast tego użyjesz krótkiego programu platformy .NET, aby obliczyć N-tą liczbę ciągu Fibonacciego.

Ciąg Fibonacciego to zbiór liczb rozpoczynający się od cyfr 0 i 1, przy czym każda kolejna liczba jest sumą dwóch poprzednich. Sekwencja będzie wyglądać następująco:

0, 1, 1, 2, 3, 5, 8, 13, 21...
  1. Otwórz plik Program.cs, wybierając go.

  2. Zastąp zawartość pliku Program.cs następującym kodem:

    int result = Fibonacci(5);
    Console.WriteLine(result);
    
    static int Fibonacci(int n)
    {
        int n1 = 0;
        int n2 = 1;
        int sum;
    
        for (int i = 2; i < n; i++)
        {
            sum = n1 + n2;
            n1 = n2;
            n2 = sum;
        }
    
        return n == 0 ? n1 : n2;
    }
    

    Uwaga

    Ten kod zawiera błąd, który będziemy debugować w dalszej części tego modułu. Nie zaleca się używania go w żadnej aplikacji Fibonacciego o znaczeniu krytycznym, dopóki ta usterka nie zostanie naprawiona.

  3. Zapisz plik, wybierając klawisze Ctrl+S w systemach Windows i Linux. Na komputerach Mac wybierz klawisze Cmd+S.

  4. Przyjrzyjmy się, jak działa zaktualizowany kod przed jego debugowaniem. Uruchom program, wprowadzając następujące polecenie w terminalu:

    dotnet run
    

    Okno terminalu ze zmodyfikowanymi danymi wyjściowymi programu.

  5. Wynik 3 jest wyświetlany w danych wyjściowych terminalu. Po zapoznaniu się z tym wykresem sekwencji Fibonacciego, który pokazuje położenie sekwencji opartej na zera dla każdej wartości w nawiasie, zobaczysz, że wynik powinien wynosić 5. Czas zapoznać się z debugerem i naprawić ten program.

    0 (0), 1 (1), 1 (2), 2 (3), 3 (4), 5 (5), 8 (6), 13 (7), 21 (8)...
    

Analizowanie problemów

  1. Uruchom program, wybierając kartę Uruchom i Debuguj po lewej stronie, a następnie wybierając przycisk Rozpocznij debugowanie . Może być konieczne najpierw wybranie przycisku Uruchom i debugowanie , a następnie wybranie pliku Program.cs .

    Zrzut ekranu przedstawiający przycisk Rozpocznij debugowanie w programie Visual Studio Code.

    Program powinien szybko zakończyć pracę. Jest to normalne, ponieważ nie dodano jeszcze żadnych punktów przerwania.

  2. Jeśli konsola debugowania nie jest wyświetlana, wybierz Ctrl+Shift+Y dla systemów Windows i Linux lub Cmd+Shift+Y dla komputerów Mac. Powinno pojawić się kilka wierszy informacji diagnostycznych z następującymi wierszami na końcu:

    ...
    Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.0\System.Threading.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
    Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.0\System.Text.Encoding.Extensions.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
    3
    The program '[88820] DotNetDebugging.dll' has exited with code 0 (0x0).
    

Wiersze w górnej części informują o tym, że domyślne ustawienia debugowania włączają opcję „Tylko mój kod”. Oznacza to, że debuger będzie debugować tylko kod i nie przejdzie do kodu źródłowego dla platformy .NET, chyba że ten tryb zostanie wyłączony. Ta opcja pozwala skupić się na debugowaniu własnego kodu.

Na końcu danych wyjściowych konsoli debugowania zobaczysz, że program zapisuje wartość 3 w konsoli, a następnie kończy działanie z kodem 0. Zazwyczaj kod zakończenia programu 0 wskazuje, że program został uruchomiony i zakończony bez awarii. Istnieje jednak różnica między awarią i zwróceniem poprawnej wartości. W tym przypadku poprosiliśmy program, aby obliczał piątą wartość sekwencji Fibonacciego:

0 (0), 1 (1), 1 (2), 2 (3), 3 (4), 5 (5), 8 (6), 13 (7), 21 (8)...

Piąta wartość na tej liście to 5, ale nasz program zwrócił wartość 3. Użyjmy debugera, aby zdiagnozować i usunąć ten błąd.

Używanie punktów przerwania i wykonywania krok po kroku

  1. Dodaj punkt przerwania, klikając lewy margines w wierszu 1 w .int result = Fibonacci(5);

    Zrzut ekranu przedstawiający lokalizację punktu przerwania w kodzie.

  2. Ponownie uruchom debugowanie. Program rozpoczyna wykonywanie. Przerywa (wstrzymuje wykonywanie) w wierszu 1 ze względu na ustawiony punkt przerwania. Użyj kontrolek debugera, aby przejść do funkcji Fibonacci().

    Zrzut ekranu przedstawiający przycisk wkraczania.

Sprawdzanie stanu zmiennych

Teraz poświęć trochę czasu na sprawdzenie wartości różnych zmiennych, używając panelu Zmienne.

Zrzut ekranu przedstawiający panel Zmienne.

  • Jaka jest pokazana wartość parametru n?
  • Jakie są wartości zmiennych lokalnych n1, n2 i sum na początku wykonywania funkcji?
  1. Następnie nastąpi przejście do pętli for przy użyciu kontrolki debugera Przekrocz.

    Zrzut ekranu przedstawiający przycisk przekroczenia.

  2. Kontynuuj przechodzenie do momentu, gdy zostanie osiągnięty pierwszy wiersz wewnątrz pętli for, w następującym wierszu:

    sum = n1 + n2;
    

Uwaga

Możesz zauważyć, że przejście przez wiersz for(...) {} wymaga wielu poleceń wykonywania krokowego. Wynika to z faktu, że w tym wierszu znajduje się wiele instrukcji. Podczas wykonywania krokowego przechodzisz do następnej instrukcji w kodzie. Zazwyczaj wiersz zawiera jedną instrukcję. Jeśli jest inaczej, musisz wykonać wiele kroków, aby przejść do następnego wiersza.

Sposób myślenia o kodzie

Ważną częścią debugowania jest zatrzymanie się i zastanowienie się nad tym, co Twoim zdaniem próbują zrobić fragmenty kodu (funkcje i bloki, takie jak pętle). Jeśli nie masz pewności, nie ma problemu — jest to część procesu debugowania. Ale aktywne zaangażowanie w proces debugowania ułatwia o wiele szybsze lokalizowanie usterek.

Przed zapoznaniem się z dalszymi szczegółami przypomnijmy, że ciąg Fibonacciego to seria liczb rozpoczynająca się od cyfr 0 i 1, w którym każda kolejna liczba jest sumą dwóch poprzednich.

Oznacza to, że:

Fibonacci(0) = 0
Fibonacci(1) = 1
Fibonacci(2) = 1 (0 + 1)
Fibonacci(3) = 2 (1 + 1)
Fibonacci(4) = 3 (1 + 2)
Fibonacci(5) = 5 (2 + 3)

Jeśli rozumiemy tę definicję i przyjrzymy się pętli for, możemy wydedukować, że:

  1. Pętla liczy od 2 do n (szukana liczba w ciągu Fibonacciego).
  2. Jeśli wartość n jest mniejsza niż 2, pętla nigdy nie zostanie uruchomiona. Instrukcja return na końcu funkcji zwróci wartość 0, jeśli n wynosi 0, i 1, jeśli n wynosi 1 lub 2. Zgodnie z definicją jest to wartość zerowa, pierwsza i druga w ciągu Fibonacciego.
  3. Bardziej interesujący przypadek występuje, gdy wartość n jest większa niż 2. W takich przypadkach bieżąca wartość jest definiowana jako suma dwóch poprzednich wartości. Dla tej pętli n1 i n2 są dwiema poprzednimi wartościami, a sum jest wartością dla bieżącej iteracji. Z tego powodu za każdym razem, gdy wyznaczymy sumę dwóch poprzednich wartości i ustawimy ją na sum, trzeba będzie zaktualizować nasze wartości n1 i n2.

W porządku, dalej nie musimy się zbytnio w to zagłębiać. Możemy w pewnym stopniu polegać na naszym debugerze. Warto jednak zastanowić się nad kodem, aby sprawdzić, czy robi to, czego oczekujemy, i wiedzieć, co zrobić, jeśli kod nie zachowuje się zgodnie z oczekiwaniami.

Znajdowanie usterki za pomocą punktów przerwania

Przechodzenie przez kod może być przydatne, ale żmudne, zwłaszcza w przypadku pracy z pętlami lub innym kodem, który jest wywoływany wielokrotnie. Zamiast wiele razy przechodzić przez pętlę, można ustawić nowy punkt przerwania w pierwszym wierszu pętli.

Podczas wykonywania tego zadania ważne jest strategiczne podejście do tego, gdzie umieszczamy nasze punkty przerwania. Szczególnie interesuje nas wartość parametru sum, ponieważ reprezentuje bieżącą maksymalną wartość Fibonacciego. Z tego powodu umieśćmy nasz punkt przerwania w wierszu po ustawieniu elementu sum.

  1. Dodaj drugi punkt przerwania w wierszu 13.

    Zrzut ekranu przedstawiający ustawianie drugiego punktu przerwania.

    Uwaga

    Jeśli zauważysz, że nadal uruchamiasz swój kod, a następnie przechodzisz przez jeden lub dwa wiersze, możesz łatwo zaktualizować punkty przerwania do efektywniejszych wierszy.

  2. Teraz, gdy w pętli został ustawiony dobry punkt przerwania, użyj kontrolki debugera Kontynuuj, aby kontynuować przechodzenie do momentu osiągnięcia punktu przerwania. Patrząc na nasze zmienne lokalne, zobaczymy następujące wiersze:

    n [int]: 5
    n1 [int]: 0
    n2 [int]: 1
    sum [int]: 1
    i [int]: 2
    

    Wszystkie te wiersze wydają się być poprawne. Podczas pierwszego przejścia przez pętlę element sum dla poprzednich dwóch wartości wynosi 1. Zamiast krokowo przechodzić przez kolejne wiersze, możemy skorzystać z naszych punktów przerwania, aby szybko przejść do następnego razu za pośrednictwem pętli.

  3. Wybierz pozycję Kontynuuj, aby kontynuować przepływ programu do momentu, gdy zostanie trafiony następny punkt przerwania, czyli podczas następnego przejścia przez pętlę.

    Uwaga

    Nie martw się zbytnio o pomijanie usterki, jeśli używasz pozycji Kontynuuj. Należy się spodziewać, że w celu znalezienia problemu często wymagane będzie kilkukrotne debugowanie kodu. Często szybszą metodą jest uruchomienie programu kilka razy zamiast ostrożnie przechodzić przez kod.

    Tym razem zobaczysz następujące wartości:

    n [int]: 5
    n1 [int]: 1
    n2 [int]: 1
    sum [int]: 2
    i [int]: 3
    

    Zastanówmy się. Czy te wartości nadal mają sens? Wygląda na to, że tak. W przypadku trzeciej liczby ciągu Fibonacciego oczekujemy, że nasz element sum będzie mieć wartość 2, i faktycznie tak jest.

  4. OK, wybierzmy pozycję Kontynuuj, aby ponownie wykonać pętlę.

    n [int]: 5
    n1 [int]: 1
    n2 [int]: 2
    sum [int]: 3
    i [int]: 4
    

    Wszystko nadal wygląda dobrze. Czwarta wartość w serii powinna wynosić 3.

  5. W tym momencie można zacząć się zastanawiać, czy może kod był przez cały czas prawidłowy, a usterka powstała tylko w Twojej wyobraźni! Przejdźmy po raz ostatni przez pętlę w tym kodzie. Wybierz pozycję Kontynuuj jeszcze jeden raz.

    Poczekaj chwilę. Program zakończył pracę i wyświetlił wartość 3. To nie jest prawidłowa wartość.

    Nie martw się. To nie niepowodzenie, to nauka. Wiemy teraz, że kod przechodzi przez pętlę prawidłowo do momentu, gdy wartość i jest równa 4, ale następnie kończy działanie przed obliczeniem końcowej wartości. Zaczynam uzyskać kilka pomysłów na to, gdzie jest usterka. Czy jesteś?

  6. Ustawmy jeszcze jeden punkt przerwania w wierszu 17, który brzmi:

    return n == 0 ? n1 : n2;
    

    Ten punkt przerwania pozwoli nam sprawdzić stan programu przed zakończeniem działania funkcji. Nauczyliśmy się już wszystkich, których możemy oczekiwać od naszych poprzednich punktów przerwania w wierszach 1 i 13, abyśmy mogli je wyczyścić.

  7. Usuń poprzednie punkty przerwania w wierszach 1 i 13. Można to zrobić, klikając je na marginesie obok numerów wierszy lub usuwając pola wyboru punktu przerwania dla wierszy 1 i 13 w okienku Punkty przerwania w lewym dolnym rogu.

    Zrzut ekranu przedstawiający punkty przerwania wymienione w okienku Punkty przerwania.

    Teraz, gdy już o wiele lepiej rozumiemy, co się dzieje, i ustawiliśmy punkt przerwania przeznaczony do wychwytywania błędnego działania naszego programu, powinniśmy być w stanie znaleźć tę usterkę.

  8. Uruchom debugera po raz ostatni.

    n [int]: 5
    n1 [int]: 2
    n2 [int]: 3
    sum [int]: 3
    

    Hmm, to nie jest prawidłowa wartość. Poprosiliśmy o wartość Fibonacci(5), a otrzymaliśmy wartość Fibonacci(4). Ta funkcja zwraca wartość n2, a każda iteracja pętli oblicza wartość sum i ustawia element n2 równy sum.

    Na podstawie tych informacji i poprzedniego przebiegu debugowania możemy zobaczyć, że pętla zakończyła się, gdy element i miał wartość 4, a nie 5.

    Przyjrzyjmy się pierwszemu wierszowi pętli for nieco dokładniej.

    for (int i = 2; i < n; i++)
    

    W porządku, poczekaj chwilę. Oznacza to, że zostanie ona zakończona, gdy tylko górna część pętli for zobaczy, że i nie jest już mniejsza niż n. Oznacza to, że kod pętli nie zostanie uruchomiony w przypadku, gdy element i jest równy n. Wygląda na to, że w zamian chcieliśmy uruchomić kod do i <= n:

    for (int i = 2; i <= n; i++)
    

    W związku z tym zaktualizowany program powinien wyglądać tak jak w tym przykładzie:

    int result = Fibonacci(5);
    Console.WriteLine(result);
    
    static int Fibonacci(int n)
    {
        int n1 = 0;
        int n2 = 1;
        int sum;
    
        for (int i = 2; i <= n; i++)
        {
            sum = n1 + n2;
            n1 = n2;
            n2 = sum;
        }
    
        return n == 0 ? n1 : n2;
    }
    
  9. Zatrzymaj sesję debugowania, jeśli jeszcze tego nie zrobiono.

  10. Następnie wprowadź poprzednią zmianę w wierszu 10 i pozostaw nasz punkt przerwania w wierszu 17.

  11. Uruchom ponownie debuger. Tym razem po osiągnięciu punktu przerwania w wierszu 17 zobaczymy następujące wartości:

    n [int]: 5
    n1 [int]: 3
    n2 [int]: 5
    sum [int]: 5
    

    Hej! Wygląda na to, że wszystko działa! Świetna robota, dzięki Tobie firma Fibonacci, Inc. rozwiązała swoje problemy.

  12. Wybierz pozycję Kontynuuj, aby upewnić się, że program zwraca poprawną wartość.

    5
    The program '[105260] DotNetDebugging.dll' has exited with code 0 (0x0).
    

    I że zwraca prawidłowe dane wyjściowe.

Udało Ci się! Część kodu nienapisana przez Ciebie została zdebugowana przy użyciu debugera platformy .NET w programie Visual Studio Code.

W następnej lekcji dowiesz się, jak ułatwić debugowanie pisanego kodu przy użyciu wbudowanych funkcji rejestrowania i śledzenia platformy .NET.