Упражнение. Отладка с помощью Visual Studio Code
Пришло время поставить недавно приобретенные знания по отладке на практике. Предположим, вы только что приступили к работе и вам нужно применить свои навыки отладки в .NET для исправления ошибки в важнейшем продукте компании — калькуляторе Фибоначчи.
Создание примера проекта .NET для отладки
Чтобы настроить Visual Studio Code для отладки в .NET, вам прежде всего нужен проект .NET. Visual Studio Code содержит встроенный терминал, который упрощает создание проектов.
В Visual Studio Code выберите Файл>Открыть папку.
Создайте в произвольном месте новую папку с именем
DotNetDebugging. Затем выберите "Выбрать папку".Откройте интегрированный терминал в Visual Studio Code, выбрав Вид>Терминал из главного меню.
Скопируйте и вставьте следующую команду в окно терминала:
dotnet new consoleЭта команда создает файл Program.cs в папке с базовой программой Hello World, уже написанной. Он также создает файл проекта C# с именем DotNetDebugging.csproj.
Чтобы запустить программу Hello World, скопируйте и вставьте следующую команду в окно терминала.
dotnet runВ окне терминала отображается "Hello, world!" в качестве выходных данных.
Настройка Visual Studio Code для отладки в .NET
Откройте Program.cs , выбрав его.
Когда вы впервые открываете файл C# в Visual Studio Code, появляется запрос на установку рекомендуемых расширений для C#. Если вы видите этот запрос, нажмите кнопку "Установить " в запросе.
Visual Studio Code установит расширение C# и отобразит еще один запрос на добавление необходимых ресурсов для сборки и отладки проекта. Нажмите кнопку "Да ".
Вы можете закрыть вкладку Extension: C# , чтобы сосредоточиться на коде, который мы отладим.
Добавление программной логики в калькулятор Фибоначчи
Пока наш проект просто выдает в консоль сообщение Hello World, и отлаживать здесь нечего. Вместо этого вы будете использовать короткую программу .NET для вычисления N-го числа последовательности Fibonacci.
Последовательность Фибоначчи — это ряд чисел, который начинается с 0 и 1, где каждое следующее число является суммой двух предыдущих. Последовательность выглядит следующим образом:
0, 1, 1, 2, 3, 5, 8, 13, 21...
Откройте Program.cs , выбрав его.
Замените содержимое Program.cs следующим кодом:
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; }Примечание.
Этот код содержит ошибку, и в этом модуле показано, как ее устранить. Мы не рекомендуем использовать этот код в реальных приложениях для вычисления последовательности Фибоначчи, пока эта ошибка не будет исправлена.
Сохраните файл, нажав клавиши CTRL+S для Windows и Linux. Выберите cmd+S для Mac.
Давайте рассмотрим, как работает обновленный код перед отладкой. Запустите программу, введя в окне терминала следующую команду:
dotnet run
Результат, 3, показан в выходных данных терминала. При просмотре этой диаграммы последовательности Фибоначчи, показывающей положение последовательности на основе нуля для каждого значения в круглых скобках, вы увидите, что результат должен быть равен 5. А значит, самое время узнать с тем, как отладчик поможет нам исправить программу.
0 (0), 1 (1), 1 (2), 2 (3), 3 (4), 5 (5), 8 (6), 13 (7), 21 (8)...
Анализ проблем
Запустите программу, выбрав вкладку "Запуск и отладка " слева, а затем нажмите кнопку "Начать отладку ". Сначала может потребоваться выбрать кнопку "Запуск и отладка ", а затем выбрать файл Program.cs .
Программа выполнится быстро. Это нормально, так как вы еще не добавили точки останова.
Если консоль отладки не отображается, выберите CTRL+SHIFT+Y для Windows и Linux или CMD+SHIFT+Y для Mac. Вы увидите несколько строк с диагностическими сведениями и следующие строки в самом конце:
... 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).
Строки в верхней части этого фрагмента информируют о том, что в параметрах отладки по умолчанию включен параметр "Только мой код". Это означает, что отладчик будет отлаживать только ваш код программы, не переходя к исходному коду .NET, пока включен этот режим. Это позволяет не отвлекаться от отладки собственного кода.
В завершающей части выходных данных на консоли отладки видно, что программа выводит в консоль результат "3" и завершает выполнение с кодом 0. Обычно код завершения 0 означает, что запущенная программа завершила работу без сбоев. Но отсутствие сбоя не означает, что программа возвращает правильное значение. В нашем примере программа должна вычислить значение пятого элемента последовательности Фибоначчи:
0 (0), 1 (1), 1 (2), 2 (3), 3 (4), 5 (5), 8 (6), 13 (7), 21 (8)...
В реальной последовательности пятый элемент имеет значение 5, но наша программа вернула значение 3. Давайте воспользуемся отладчиком для диагностики и устранения этой проблемы.
Использование точек останова и пошаговое выполнение
Добавьте точку останова, щелкнув в левом поле на строке 1.
int result = Fibonacci(5);
Снова начните отладку. Программа начнет выполнение. Оно прервется (приостановится) на строке 1, так как вы установили точку останова. С помощью элементов управления перейдите в функцию
Fibonacci().
Проверка состояния переменных
Теперь потребуется некоторое время для проверки значений различных переменных с помощью панели "Переменные ".
- Какое значение отображается для параметра
n? - Какие значения имеют локальные переменные
n1,n2иsumв начале выполнения функции?
Затем мы перейдем к циклу
forс помощью команды отладчика Step Over.
Продолжайте выполнять эти шаги, пока не дойдете до первой строки в цикле
for, которая выглядит так:sum = n1 + n2;
Примечание.
Возможно, вы заметили, что для перемещения по строке for(...) {} требуется несколько команд выполнения шага с заходом. Эта ситуация возникает из-за нескольких операторов в этой строке. При пошаговом выполнении вы переходите к следующему оператору в коде. Обычно существует один оператор на строку. Но если это не так, вам нужно выполнить несколько шагов, чтобы перейти к следующей строке.
Подумайте о коде
Важная часть отладки — остановиться и на основании имеющейся информации предположить, что пытаются делать части кода (и функции, и блоки, например циклы). Ничего страшного, если вы не уверены, это часть процесса отладки. Но активное участие в процессе отладки поможет вам гораздо быстрее обнаруживать ошибки.
Прежде чем продолжать работу, давайте вспомним теорию. Последовательность Фибоначчи — это ряд чисел, который начинается с 0 и 1, где каждое следующее число является суммой двух предыдущих.
Это означает, что:
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)
Зная это определение, мы можем сделать следующие выводы при анализе цикла for:
- Цикл ведет отсчет от 2 до
n(нужное число в последовательности Фибоначчи). - Если значение
nменьше 2, цикл не выполняется. Операторreturnв конце функции вернет значение 0, еслиnравно 0, или 1, еслиnравно 1 или 2. Это по определению нулевое, первое и второе значения последовательности Фибоначчи. - Самое интересное начинается, когда значение
nбольше 2. В этом случае текущее значение определяется как сумма двух предыдущих значений. В нашем циклеn1иn2обозначают два предыдущих значения, аsum— вычисляемое значение в текущей итерации. При этом каждый раз вычисляется сумма двух предыдущих значений, результат сохраняется вsum, а затем обновляются значенияn1иn2.
Этой информации будет достаточно. Мы можем положиться на наш отладчик. Тем не менее размышлять о коде полезно, чтобы понять, выполняется ли он ожидаемым образом, и быть готовым к ситуации, когда это не так.
Обнаружение ошибки с помощью точек останова
Пошаговое выполнение кода может оказаться полезным, но емким, особенно при работе с циклами или другим кодом, который вызывается многократно. Чтобы не проходить цикл снова и снова, мы можем установить новую точку останова в первой строке цикла.
На этом этапе важно правильно выбрать стратегию размещения точек останова. Мы особенно заинтересованы в значении sum, так как он представляет текущее максимальное значение Fibonacci. Давайте из-за этого поместим точку останова в строку послеsum того, как она будет установлена.
Добавьте еще одну точку останова в строке 13.
Примечание.
Если вы заметили, что продолжаете выполнять свой код, а затем переходите на одну или две строки, вы можете переместить точки останова на более релевантные строки.
Теперь, когда у нас есть хорошая точка останова в цикле, используйте управление отладчиком Продолжить, чтобы продолжать до тех пор, пока точка останова не будет достигнута. Проверяя локальные переменные, мы видим следующие строки:
n [int]: 5 n1 [int]: 0 n2 [int]: 1 sum [int]: 1 i [int]: 2Все эти строки кажутся правильными. При первом прохождении цикла сумма (
sum) двух предыдущих значений равна 1. И вместо того, чтобы проходить цикл построчно, мы можем воспользоваться нашими точками останова, чтобы перейти к следующему циклу.Нажмите кнопку "Продолжить , чтобы продолжить поток программы" до тех пор, пока не будет достигнута следующая точка останова, которая будет находиться на следующем проходе через цикл.
Примечание.
Не беспокойтесь о пропуске ошибки при использовании "Продолжить". Вы будете часто выполнять отладку кода несколько раз, чтобы найти ошибку. Обычно быстрее будет несколько раз выполнить такую отладку, чем проявлять чрезмерную осторожность и выполнять код пошагово.
На этот раз мы видим следующее:
n [int]: 5 n1 [int]: 1 n2 [int]: 1 sum [int]: 2 i [int]: 3Давайте поразмыслим. Имеют ли смысл эти значения? Пока все выглядит нормально. Мы ожидаем, что третье число в последовательности Фибоначчи равно 2, и переменная
sumимеет именно это значение.Хорошо, давайте снова нажмите кнопку "Продолжить ".
n [int]: 5 n1 [int]: 1 n2 [int]: 2 sum [int]: 3 i [int]: 4И снова все выглядит неплохо. Четвертый элемент нашей последовательности должен быть равен 3.
Возможно, вы уже сомневаетесь в том, что в коде была ошибка, ведь все работает правильно! Давайте не торопиться с выводами и пройдем последнюю итерацию цикла. Нажмите кнопку "Продолжить еще раз".
Но что это? Программа завершила работу и вернула значение 3! Это неправильно.
Но не беспокойтесь. Это не провал, а всего лишь новый урок. Теперь мы знаем, что код правильно повторяет цикл до тех пор, пока не значение
iне будет равно 4, после чего выполнение завершается без вычисления итогового значения. Я начинаю получать некоторые идеи о том, где ошибка. А Вы?Давайте установим еще одну точку останова на строке 17 со следующим кодом:
return n == 0 ? n1 : n2;Эта точка останова позволит нам проверить состояние программы перед выходом из функции. Мы уже проверили все, что можно проверить на предыдущих точках останова (строки 1 и 13), и теперь их можно удалить.
Удалите предыдущие точки останова в строках 1 и 13. Это можно сделать, щелкнув их в поле рядом с номерами строк, или сняв флажки для точек останова для строк 1 и 13 в области точек останова в левом нижнем углу.
Теперь мы намного лучше понимаем, что происходит. А установив точку останова, предназначенную для того, чтобы отследить момент неправильного выполнения программы, мы сможем перехватить ошибку!
Запустите отладчик в последний раз.
n [int]: 5 n1 [int]: 2 n2 [int]: 3 sum [int]: 3Вот и наша ошибка. Мы запрашивали значение Fibonaccci(5), а получили Fibonacci(4). Эта функция возвращает
n2, а каждая итерация цикла вычисляет значениеsumи задает дляn2значениеsum.На основе этих сведений и результатов предыдущего сеанса отладки можно понять, что цикл завершил работу, когда значение
iбыло равно 4, а не 5.Давайте подробнее рассмотрим первую строку цикла
for.for (int i = 2; i < n; i++)Но что это? Это означает, что он завершит работу, как только верхняя часть цикла for увидит, что значение
iбольше не меньшеn. Это означает, что код цикла не будет выполняться, еслиiравноn. Вместо этого нам нужно было, чтобы цикл выполнялся доi <= n.for (int i = 2; i <= n; i++)После исправления программа будет выглядеть так:
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; }Закройте сеанс отладки, если вы еще это не сделали.
Затем сделайте предыдущее изменение на строку 10 и оставьте точку останова в строке 17.
Перезапустите отладчик. Теперь при достижении точки останова на строке 17 мы увидим следующие значения:
n [int]: 5 n1 [int]: 3 n2 [int]: 5 sum [int]: 5Эй! Кажется, мы все починили! Отличная работа, вы спасли ситуацию для Фибоначчи, Инк.!
Нажмите кнопку "Продолжить" , чтобы убедиться, что программа возвращает правильное значение.
5 The program '[105260] DotNetDebugging.dll' has exited with code 0 (0x0).Выходные данные правильные.
Вы справились! Вы отладили фрагмент чужого кода, используя отладчик .NET в Visual Studio Code.
В следующем уроке показано, как сделать свой код более пригодным к отладке с помощью функций ведения журнала и трассировки, встроенных в .NET.