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 przyciskuRozpocznij 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.
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.
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.
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 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
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.
Wybierz przycisk Uruchom ponownie 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).
Umieść kursor na zmiennej
GalaxyType
po prawej stronie, a następnie po lewej stronie ikony klucza rozwiń pozycjętheGalaxy.GalaxyType
. Zobaczysz, żeGalaxyType
zawiera właściwośćMyGType
, a wartość właściwości jest ustawiona naSpiral
wartość ."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.
W tym samym kodzie, podczas debugowania, umieść kursor na końcu
theGalaxy.GalaxyType
i zmień go natheGalaxy.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).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.
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 ponownie.
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 elementuMyGType
.Mimo że ustawiamy każdą galaktykę z obiektem typu
GType
(który maMyGType
właściwość), debuger nie rozpoznajetheGalaxy
obiektu jako obiektu typuGType
. Co się dzieje? Chcesz przejrzeć dowolny kod, który ustawia typ galaktyki. Gdy to zrobisz, zobaczysz, żeGType
klasa na pewno ma właściwośćMyGType
, ale coś nie jest w porządku. Komunikatobject
o błędzie dotyczący okazuje się być wskazówką. Dla interpretera języka typ wydaje się być obiektem typuobject
zamiast obiektu typuGType
.Przeglądając kod związany z ustawieniem typu galaktyki, można znaleźć
GalaxyType
właściwośćGalaxy
klasy jest określona jakoobject
zamiastGType
.public object GalaxyType { get; set; }
Zmień poprzedni kod w następujący sposób:
public GType GalaxyType { get; set; }
Wybierz przycisk Uruchom ponownie 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 natheGalaxy.GalaxyType.MyGType
, i zobaczyć, że wartość jest poprawnie ustawiona.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
Ustaw punkt przerwania w tym wierszu kodu przed instrukcją (przed instrukcją
switch
Select
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.
Wybierz przycisk Uruchom ponownie na pasku narzędzi debugowania (Ctrl Shift + + F5), aby ponownie uruchomić.
Debuger wstrzymuje się w wierszu kodu, w którym ustawiono punkt przerwania.
Umieść kursor na zmiennej
type
. Zostanie wyświetlonaS
wartość (po kodzie znaku). Interesuje Cię wartośćI
, ponieważ wiesz, że jest to nieregularny typ galaktyki.Naciśnij klawisz F5 i ponownie umieść kursor nad zmienną
type
. Powtórz ten krok do momentu wyświetlenia wartościI
w zmiennejtype
.Teraz naciśnij klawisz F11 (Debuguj>krok do).
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 ustawianyMyGType
jako typ galaktyki Nieregularny, ale debuger pomija ten kod całkowicie i wstrzymuje się wdefault
sekcjiswitch
instrukcji (Else
instrukcja w Visual Basic).Patrząc na kod, w instrukcji zobaczysz literówkę
case 'l'
. Powinna ona mieć wartośćcase 'I'
.Wybierz element w kodzie
case 'l'
i zastąp go ciągiemcase 'I'
.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.