Отладка для абсолютных начинающих
Независимо от обстоятельств код, создаваемый разработчиками программного обеспечения, далеко не всегда работает так, как задумано. В некоторых случаях все идет совершенно не по плану! Когда происходит непредвиденное, следующая задача заключается в том, чтобы выяснить, почему, и хотя мы можем просто заманчиво просто следить за нашим кодом в течение нескольких часов, проще и эффективнее использовать средство отладки или отладчик.
К сожалению, отладчик не является той волшебной палочкой, по мановению которой будут выявлены абсолютно все проблемы в коде. Процесс отладки подразумевает пошаговое выполнение кода в средстве отладки (например, в Visual Studio) в поисках точки, в которой вы допустили ошибку при написании программы. Таким образом вы узнаете, какие исправления нужно внести в код. При этом средства отладки часто позволяют вносить временные изменения, чтобы продолжить работу с программой.
Эффективное использование отладчика также требует определенных навыков, которые вырабатываются только с практикой, однако умение работать с ним является основополагающим требованием к любому разработчику программного обеспечения. В этой статье мы представим основные принципы отладки и некоторые начальные рекомендации.
Проанализируйте проблему, задавая себе правильные вопросы
Это поможет вам выяснить, в чем состоит проблема, прежде чем приступать к ее решению. Мы полагаем, что вы уже сталкивались с проблемами в коде, иначе вряд ли читали бы сейчас эту статью в поисках советов по его отладке! Итак, прежде чем начать отладку, проанализируйте проблему, которую вы пытаетесь решить:
Что именно должен был выполнить код?
Что произошло на самом деле?
Если при запуске приложения возникает ошибка (исключение), это может быть хорошо! Исключение возникает в том случае, если при выполнении кода происходит непредвиденное событие (как правило, это ошибка какого-либо рода). С помощью средства отладки вы можете перейти точно к тому месту в коде, где возникло исключение, и исследовать возможные способы исправления ситуации.
Если произошло что-то еще, каковы признаки проблемы? Есть ли у вас предположения относительно того, в каком месте кода возникла проблема? Например, если код должен выводить какой-то текст, но при этом текст содержит ошибки, вы можете сделать вывод, что в этом случае используются неверные данные или код вывода текста содержит ошибки другого рода. При пошаговом выполнении кода в отладчике вы можете изучить каждое изменение переменных и точно определить, когда и каким образом были присвоены неверные значения.
Проверьте свои предположения
Прежде чем исследовать причины возникновения ошибки, проверьте предположения, на основании которых вы ожидаете тот или иной результат. Неявные или неизвестные вам предположения могут помешать выявлению проблемы, даже если вы прямо смотрите на ее причину в отладчике. У вас может быть длинный список возможных предположений! Чтобы проверить их, задайте себе несколько вопросов.
Используете ли вы нужный API (то есть соответствующие объект, функцию, метод или свойство)? Возможно, используемый вами API работает не так, как вы ожидаете. (После проверки вызова API в отладчике исправление может потребовать поездки в документацию, чтобы определить правильный API.)
Правильно ли вы используете API? Даже если вы выбрали нужный API, он может использоваться неправильно.
Нет ли в вашем коде опечаток? Некоторые опечатки, например ошибки в написании имени переменной, могут быть незаметными, особенно при работе с языками, в которых не требуется объявление переменных перед их использованием.
Вносили ли вы в код изменения, полагая, что они никак не связаны с возникшей проблемой?
Должны ли объект или переменная содержать определенное значение (или определенный тип значения) и соответствует ли это действительности?
Известно ли назначение кода? Отлаживать чужой код часто бывает сложнее. Если это не ваш код, возможно, для его эффективной отладки вам потребуется изучить, что он делает.
Совет
При написании кода начинайте с небольших и гарантированно работающих фрагментов! (Хороший пример кода полезен здесь.) Иногда проще исправить большой или сложный набор кода, начиная с небольшого фрагмента кода, демонстрирующего основную задачу, которую вы пытаетесь достичь. Затем вы можете последовательно изменять или добавлять код в поисках точки возникновения ошибки.
Подспрошив предположения, вы можете сократить время, необходимое для поиска проблемы в коде. Вы также можете сократить время, необходимое для устранения проблемы.
Используйте режим пошагового выполнения во время отладки для поиска места возникновения проблемы.
Очевидно, что ошибки и неверные результаты можно увидеть только после выполнения кода приложения. Кроме того, работа программы может завершиться неожиданно без каких-либо сообщений.
При запуске приложения в отладчике, который также называется режимом отладки, отладчик активно отслеживает все, что происходит при запуске программы. Кроме того, вы можете в любой точке приостановить работу приложения, исследовать его состояние и пошагово выполнять код в каждой строке, чтобы видеть в деталях, что происходит.
В Visual Studio введите режим отладки с помощью F5 (или> команды меню "Запуск отладки" или кнопки "Начать отладку" на панели инструментов отладки). Если возникает исключение, помощник по исправлению ошибок Visual Studio направит вас к точке его появления и предоставит другую необходимую информацию. См. дополнительные сведения об обработке исключений в коде в разделе Приемы и инструменты отладки.
Если исключение не возникает, возможно, вы имеете некоторое представление о том, где искать проблему в коде. Этот шаг заключается в том, что вы используете точки останова с отладчиком, чтобы дать себе возможность тщательно изучить код. Точки останова — это самая основная и важная функция надежной отладки. Точка останова указывает, в каком месте Visual Studio следует приостановить выполнение, чтобы вы могли проверить значения переменных, работу памяти или последовательность выполнения кода.
Чтобы задать точку останова в Visual Studio, достаточно щелкнуть в левом поле рядом с интересующей вас строкой кода. Также для этого можно поместить указатель мыши в нужную строку и нажать клавишу F9.
Чтобы продемонстрировать это, мы рассмотрим пример кода, который уже содержит несколько ошибок. Здесь мы используем C#, однако функции отладки также применяются для Visual Basic, C++, JavaScript, Python и других поддерживаемых языков. Также предоставлен пример кода для Visual Basic, но снимки экрана приведены для C#.
Создание образца приложения с ошибками
Затем вы создадите приложение с несколькими ошибками.
Необходимо установить Visual Studio и рабочую нагрузку разработки для классических приложений .NET.
Установите Visual Studio бесплатно со страницы скачиваемых материалов Visual Studio, если еще не сделали этого.
Если необходимо установить рабочую нагрузку, но у вас уже есть Visual Studio, выберите "Сервис>получения инструментов" и "Компоненты". Запускается Visual Studio Installer. Выберите рабочую нагрузку Разработка классических приложений .NET и нажмите Изменить.
Откройте Visual Studio.
На начальном экране выберите Создать проект. Введите консоль в поле поиска, выберите C# или Visual Basic в качестве языка, а затем выберите консольное приложение для .NET. Нажмите кнопку Далее. Введите имя проекта, например ConsoleApp_FirstApp , и нажмите кнопку "Далее".
Выберите рекомендуемую целевую платформу или .NET 8, а затем нажмите кнопку "Создать".
Если вы не видите шаблон проекта консольного приложения для .NET, перейдите в раздел "Сервис>получения инструментов и функций", который открывает установщик Visual Studio. Выберите рабочую нагрузку Разработка классических приложений .NET и нажмите Изменить.
Visual Studio создаст консольный проект и откроет его в Обозревателе решений в области справа.
Откройте файл Program.cs (или Program.vb) и замените все его содержимое по умолчанию следующим кодом. (Сначала выберите вкладку для нужного языка: C# или 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} } }
Этот код выводит список, содержащий название галактики, расстояние до нее, а также тип галактики. При отладке важно учитывать предназначение кода. Ниже показан формат одной строки из списка, который мы хотим отобразить в выводе:
название галактики, расстояние, тип галактики.
Выполнить приложение
Нажмите клавишу F5 или кнопку "Начать отладку" на панели инструментов отладки, расположенной над редактором кода.
По результатам запуска приложения отладчик не демонстрирует никаких исключений. Тем не менее данные, выводимые в окне консоли, не соответствуют ожиданиям. Вот что мы должны были увидеть:
Tadpole 400, Spiral
Pinwheel 25, Spiral
Cartwheel, 500, Lenticular
Small Magellanic Cloud .2, Irregular
Andromeda 3, Spiral
Maffei 1, Elliptical
Но вместо этого вы увидите следующие выходные данные:
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
По выходным данным и коду мы видим, что GType
— это имя класса, содержащего тип галактики. Но мы пытаемся отобразить сам тип (например, Spiral, то есть спиральная), а не имя класса!
Отладка приложения
Пока приложение еще работает, вставьте точку останова.
Щелкните правой кнопкой мыши рядом с
Console.WriteLine
методом, чтобы получить контекстное меню и выберите точку останова вставить точку> останова в всплывающем меню.foreach (Galaxy theGalaxy in theGalaxies) { Console.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears + ", " + theGalaxy.GalaxyType); }
В месте установки точки останова в левом поле появится красный круг.
При возникновении проблемы в выходных данных вы начинаете отладку, просматривая предыдущий код, который задает выходные данные в отладчике.
Нажмите кнопку "Перезапустить" на панели инструментов отладки (CTRL + SHIFT + F5).
Выполнение приложения приостановится в заданной точке останова. Место приостановки отладчика будет выделено желтым цветом (при этом желтая строка кода еще не выполнена).
Наведите указатель мыши на переменную
GalaxyType
справа. После этого развернитеtheGalaxy.GalaxyType
слева от значка гаечного ключа. Как вы можете видеть,GalaxyType
содержит свойствоMyGType
, которому присваивается значениеSpiral
.Изначально вы ожидаете, что в окно консоли будет выведено именно значение Spiral. Поэтому вы можете получить доступ к значению в этом коде при запуске приложения. В этом сценарии мы используем неверный API-интерфейс. Давайте посмотрим, можно ли исправить это при выполнении кода в отладчике.
Во время отладки этого же кода установите указатель мыши в конец элемента
theGalaxy.GalaxyType
и измените его наtheGalaxy.GalaxyType.MyGType
. Хотя вы можете внести изменения, редактор кода показывает ошибку, указывающую, что он не может скомпилировать этот код. (В Visual Basic ошибка не отображается, и этот раздел кода работает.)Нажмите клавишу F11 (Шаг отладки>или кнопка "Шаг в шаг" на панели инструментов отладки), чтобы выполнить текущую строку кода.
При нажатии клавиши F11 отладчик переходит на одну инструкцию вперед и выполняет соответствующий код. F10 (Step Over) — это аналогичная команда, и оба полезны при обучении использованию отладчика.
Откроется диалоговое окно "Изменить и продолжить", указывающее, что изменения не могут быть скомпилированы.
Примечание.
Для отладки кода примера Visual Basic пропустите следующие несколько шагов, пока не будет указано, как нажать кнопку "Перезапустить".
Выберите "Изменить" в окне сообщения "Изменить и продолжить". Сообщение об ошибке будет выведено в окне Список ошибок. Эта ошибка указывает, что
'object'
не содержит определение дляMyGType
.Хотя мы задаем каждую галактику как объект типа
GType
(имеющий свойствоMyGType
), отладчик не считаетtheGalaxy
объектом типаGType
. Почему? В этом случае необходимо проверить код, который задает тип галактики. Вы увидите, что классGType
содержит свойствоMyGType
, однако все же что-то работает не так. Разгадка кроется в сообщении об ошибке дляobject
. Интерпретатор языка воспринимает его как объектobject
вместо ожидаемого типаGType
.Анализируя код, в котором задается тип галактики, вы можете увидеть, что свойство
GalaxyType
классаGalaxy
задается какobject
вместоGType
.public object GalaxyType { get; set; }
Измените предыдущий код следующим образом:
public GType GalaxyType { get; set; }
Нажмите кнопку "Перезапустить" на панели инструментов отладки (CTRL + SHIFT + F5), чтобы перекомпилировать код и перезапустить.
Когда отладчик приостановит выполнение на строке
Console.WriteLine
, вы можете навести указатель мыши наtheGalaxy.GalaxyType.MyGType
и убедиться, что значение задано правильно.Удалите точку останова, щелкнув ее кружок в левом поле (также для этого можно выбрать команду Точка останова>Удалить точку останова). После этого нажмите клавишу F5 для продолжения.
Приложение запускается и отображает выходные данные. Это выглядит хорошо, но вы заметили одно. Вы ожидали, что небольшая галактика Magellanic Cloud будет отображаться как нерегулярная галактика в выходных данных консоли, но она не показывает вообще типа галактики.
Tadpole 400, Spiral Pinwheel 25, Spiral Cartwheel, 500, Lenticular Small Magellanic Cloud .2, Andromeda 3, Spiral Maffei 1, Elliptical
Установите точку останова на этой строке кода перед инструкцией
switch
(инструкциейSelect
в Visual Basic).public GType(char type)
Здесь задается тип галактики, поэтому нам необходимо изучить эту строку более пристально.
Нажмите кнопку "Перезапустить" на панели инструментов отладки (CTRL + SHIFT + F5), чтобы перезапустить.
Отладчик приостановит работу в строке кода, где вы задали точку останова.
Наведите указатель мыши на переменную
type
. Отображается значениеS
(после кода символа). Вас интересует значениеI
, поскольку эта галактика должна иметь тип Irregular (неправильная).Нажмите клавишу F5 и снова наведите указатель мыши на переменную
type
. Повторяйте этот шаг, пока в переменнойtype
не появится значениеI
.Теперь нажмите клавишу F11 (шаг отладки>).
Нажимайте клавишу F11 до тех пор, пока вы не остановитесь в строке кода с инструкцией
switch
для значения "I" (инструкцияSelect
для Visual Basic). Здесь вы увидите очевидную ошибку, связанную с опечаткой. Вы ожидали, что код будет выполнен дальше до места, где дляMyGType
задается тип галактики Irregular, однако вместо этого отладчик полностью пропускает этот код и приостанавливает работу в разделеdefault
инструкцииswitch
(инструкцияElse
для Visual Basic).Взглянув на код, вы заметите опечатку в инструкции
case 'l'
. Оно должно иметь значениеcase 'I'
.Выберите в коде и
case 'l'
замените егоcase 'I'
на .Удалите точку останова и нажмите кнопку "Перезапустить ", чтобы перезапустить приложение.
Теперь все ошибки исправлены и приложение выдает ожидаемые результаты.
Нажмите любую клавишу, чтобы завершить работу приложения.
Итоги
Если вы сталкиваетесь с проблемой, воспользуйтесь отладчиком и командами пошагового выполнения, такими как F10 и F11, для поиска области кода, в которой возникают ошибки.
Примечание.
Если выявить проблемную область кода не удается, задайте точку останова в коде, который выполняется перед возникновением ошибки, после чего используйте команды пошагового выполнения, пока проблема не проявится. Также вы можете использовать точки трассировки для вывода сообщений в окно вывода. Анализируя сообщения, которые выводятся в окно или отсутствуют в нем, нередко можно изолировать область кода, где возникает проблема. Для дальнейшего сужения области поиска этот процесс может потребоваться повторить несколько раз.
Выявив проблемную область кода, используйте отладчик для ее детального анализа. Чтобы определить причину возникновения проблемы, проверьте код во время выполнения приложения в отладчике.
Проверьте переменные и убедитесь, что они содержат значения того типа, который вы ожидаете. Если переменная содержит недопустимое значение, найдите, где оно было задано. Для этого может потребоваться перезапустить отладчик, проверить стек вызовов или выполнить одновременно оба этих действия.
Проверьте, выполняет ли ваше приложение код, который вы ожидаете. (Так, в примере приложения должна была выполняться инструкция
switch
, задающая тип галактики Irregular, однако нужный код был пропущен из-за опечатки.)
Совет
Отладчик представляет собой эффективное средства для выявления ошибок. Средство отладки может искать ошибки вместо вас в том случае, если ему известно предназначение кода. Для этого вы должны указать предназначение. Этого можно добиться с помощью модульных тестов.
Следующие шаги
Из этой статьи вы узнали общие принципы отладки приложений. Теперь вы можете приступить к изучению других возможностей отладчика.