Упражнение. Отладка с помощью Visual Studio Code

Завершено

Пришло время поставить недавно приобретенные знания по отладке на практике. Предположим, вы только что приступили к работе и вам нужно применить свои навыки отладки в .NET для исправления ошибки в важнейшем продукте компании — калькуляторе Фибоначчи.

Создание примера проекта .NET для отладки

Чтобы настроить Visual Studio Code для отладки в .NET, вам прежде всего нужен проект .NET. Visual Studio Code содержит встроенный терминал, который упрощает создание проектов.

  1. В Visual Studio Code выберите пункты меню Файл>Открыть папку.

  2. Создайте в произвольном месте новую папку с именем DotNetDebugging. Затем выберите Выбрать папку.

  3. Откройте интегрированный терминал Visual Studio Code, выбрав Вид>Терминал в главном меню.

  4. Скопируйте и вставьте следующую команду в окно терминала:

    dotnet new console
    

    Эта команда создает файл Program.cs в папке с простой программой Hello World, которая была написана ранее. Она также создает файл проекта C# с именем dotNetDebugging.csproj.

  5. Чтобы запустить программу Hello World, скопируйте и вставьте следующую команду в окно терминала.

    dotnet run
    

    В окне терминала отображается "Hello, world!" в качестве выходных данных.

Настройка Visual Studio Code для отладки в .NET

  1. Откройте файл Program.cs, щелкнув его.

  2. Когда вы впервые открываете файл C# в Visual Studio Code, появляется запрос на установку рекомендуемых расширений для C#. Когда этот запрос появится, нажмите Установить.

    Screenshot of Visual Studio Code prompt to install the C# extension.

  3. Visual Studio Code установит расширение C# и отобразит еще один запрос на добавление необходимых ресурсов для сборки и отладки проекта. Нажмите кнопку Да.

    Screenshot of Visual Studio Code prompt to add required assets to build and debug your .NET project.

  4. Вы можете закрыть вкладку Extension: C# , чтобы сосредоточиться на коде, который мы отладим.

Добавление программной логики в калькулятор Фибоначчи

Пока наш проект просто выдает в консоль сообщение Hello World, и отлаживать здесь нечего. Вместо этого вы запустите короткую программу .NET, которая вычисляет элемент последовательности Фибоначчи с номером N.

Последовательность Фибоначчи — это ряд чисел, который начинается с 0 и 1, где каждое следующее число является суммой двух предыдущих. Последовательность выглядит следующим образом:

0, 1, 1, 2, 3, 5, 8, 13, 21...
  1. Откройте файл Program.cs, щелкнув его.

  2. Замените содержимое файла 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;
    }
    

    Примечание.

    Этот код содержит ошибку, и в этом модуле показано, как ее устранить. Мы не рекомендуем использовать этот код в реальных приложениях для вычисления последовательности Фибоначчи, пока эта ошибка не будет исправлена.

  3. Сохраните файл, нажав CTRL + S в Windows и Linux. На Mac нажмите CMD + S.

  4. Давайте рассмотрим, как работает обновленный код перед отладкой. Запустите программу, введя в окне терминала следующую команду:

    dotnet run
    

    Terminal window with modified program output.

  5. Результат, 3, показан в выходных данных терминала. При просмотре этой диаграммы последовательности Фибоначчи, показывающей положение последовательности на основе нуля для каждого значения в круглых скобках, вы увидите, что результат должен быть равен 5. А значит, самое время узнать с тем, как отладчик поможет нам исправить программу.

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

Анализ проблем

  1. Запустите программу, выбрав вкладку "Запуск и отладка " слева, а затем нажмите кнопку "Начать отладку ".

    Screenshot of the Start debugging button in Visual Studio Code.

    Программа выполнится быстро. Это нормально, так как вы еще не добавили точки останова.

  2. Если консоль отладки не отображается, выберите 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. Добавьте точку останова, щелкнув в левом поле строку 1 (int result = Fibonacci(5);).

    Screenshot of the breakpoint location in the code.

  2. Снова начните отладку. Программа начнет выполнение. Оно прервется (приостановится) на строке 1, так как вы установили точку останова. С помощью элементов управления перейдите в функцию Fibonacci().

    Screenshot of the Step into button.

Проверка состояния переменных

Теперь проверьте значения различных переменных с помощью панели Переменные.

Screenshot of the Variables panel.

  • Какое значение отображается для параметра n?
  • Какие значения имеют локальные переменные n1, n2 и sum в начале выполнения функции?
  1. Теперь мы перейдем в цикл for с помощью элемента управления Шаг с обходом в отладчике.

    Screenshot of the Step over button.

  2. Продолжайте выполнять эти шаги, пока не дойдете до первой строки в цикле 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:

  1. Цикл ведет отсчет от 2 до n (нужное число в последовательности Фибоначчи).
  2. Если значение n меньше 2, цикл не выполняется. Оператор return в конце функции вернет значение 0, если n равно 0, или 1, если n равно 1 или 2. Это по определению нулевое, первое и второе значения последовательности Фибоначчи.
  3. Самое интересное начинается, когда значение n больше 2. В этом случае текущее значение определяется как сумма двух предыдущих значений. В нашем цикле n1 и n2 обозначают два предыдущих значения, а sum — вычисляемое значение в текущей итерации. При этом каждый раз вычисляется сумма двух предыдущих значений, результат сохраняется в sum, а затем обновляются значения n1 и n2.

Этой информации будет достаточно. Мы можем положиться на наш отладчик. Тем не менее размышлять о коде полезно, чтобы понять, выполняется ли он ожидаемым образом, и быть готовым к ситуации, когда это не так.

Обнаружение ошибки с помощью точек останова

Пошаговое выполнение кода может оказаться полезным, но емким, особенно при работе с циклами или другим кодом, который вызывается многократно. Чтобы не проходить цикл снова и снова, мы можем установить новую точку останова в первой строке цикла.

На этом этапе важно правильно выбрать стратегию размещения точек останова. Мы особенно заинтересованы в значении sum, так как он представляет текущее максимальное значение Fibonacci. Учитывая это, мы разместим точку останова в строке послеsum присвоения значения.

  1. Добавьте еще одну точку останова в строке 13.

    Screenshot showing a second breakpoint being set.

    Примечание.

    Если вы заметили, что продолжаете выполнять свой код, а затем переходите на одну или две строки, вы можете переместить точки останова на более релевантные строки.

  2. Теперь в нашем цикле есть хорошая точка останова, и мы можем с помощью элемента управления Продолжить отладчика перейти к ней. Проверяя локальные переменные, мы видим следующие строки:

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

    Все эти строки кажутся правильными. При первом прохождении цикла сумма (sum) двух предыдущих значений равна 1. И вместо того, чтобы проходить цикл построчно, мы можем воспользоваться нашими точками останова, чтобы перейти к следующему циклу.

  3. Нажмите кнопку Продолжить, чтобы программа выполнялась до достижения следующей точки останова в следующем проходе цикла.

    Примечание.

    Не бойтесь, что вы пропустите ошибку, нажимая кнопку Продолжить. Вы будете часто выполнять отладку кода несколько раз, чтобы найти ошибку. Обычно быстрее будет несколько раз выполнить такую отладку, чем проявлять чрезмерную осторожность и выполнять код пошагово.

    На этот раз мы видим следующее:

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

    Давайте поразмыслим. Имеют ли смысл эти значения? Пока все выглядит нормально. Мы ожидаем, что третье число в последовательности Фибоначчи равно 2, и переменная sum имеет именно это значение.

  4. Но давайте нажмем кнопку Продолжить еще раз.

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

    И снова все выглядит неплохо. Четвертый элемент нашей последовательности должен быть равен 3.

  5. Возможно, вы уже сомневаетесь в том, что в коде была ошибка, ведь все работает правильно! Давайте не торопиться с выводами и пройдем последнюю итерацию цикла. Нажмите кнопку Продолжить еще раз.

    Но что это? Программа завершила работу и вернула значение 3! Это неправильно.

    Но не беспокойтесь. Это не провал, а всего лишь новый урок. Теперь мы знаем, что код правильно повторяет цикл до тех пор, пока не значение i не будет равно 4, после чего выполнение завершается без вычисления итогового значения. Я начинаю получать некоторые идеи о том, где ошибка. А Вы?

  6. Давайте установим еще одну точку останова на строке 17 со следующим кодом:

    return n == 0 ? n1 : n2;
    

    Эта точка останова позволит нам проверить состояние программы перед выходом из функции. Мы уже проверили все, что можно проверить на предыдущих точках останова (строки 1 и 13), и теперь их можно удалить.

  7. Удалите предыдущие точки останова в строках 1 и 13. Это можно сделать, щелкнув их в поле рядом с номерами строк или снимите флажки точки останова проверка для строк 1 и 13 в области точек останова в левом нижнем левом.

    Screenshot showing the breakpoints listed in the Breakpoints pane.

    Теперь мы намного лучше понимаем, что происходит. А установив точку останова, предназначенную для того, чтобы отследить момент неправильного выполнения программы, мы сможем перехватить ошибку!

  8. Запустите отладчик в последний раз.

    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;
    }
    
  9. Закройте сеанс отладки, если вы еще это не сделали.

  10. Затем сделайте предыдущее изменение на строку 10 и оставьте точку останова в строке 17.

  11. Перезапустите отладчик. Теперь при достижении точки останова на строке 17 мы увидим следующие значения:

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

    Эй! Кажется, мы все починили! Отличная работа, корпорация Фибоначчи спасена.

  12. Нажмите кнопку Продолжить и убедитесь, что программа возвращает правильное значение.

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

    Выходные данные правильные.

Вы справились! Вы отладили фрагмент чужого кода, используя отладчик .NET в Visual Studio Code.

В следующем уроке показано, как сделать свой код более пригодным к отладке с помощью функций ведения журнала и трассировки, встроенных в .NET.