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

Завершено

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

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

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

  1. В Visual Studio выберите Файл>Создать>Проект.

  2. В диалоговом окне Создание проекта выберите Консольное приложение и нажмите Далее.

  3. Назовите проект DotNetDebuggingи выберите расположение, в котором требуется его сохранить. Оставьте остальные значения по умолчанию, а затем нажмите кнопку "Далее".

  4. На последнем экране нажмите кнопку Создать.

Visual Studio создаст для нас консольный проект с помощью выбранного нами шаблона. После загрузки проекта откройте файл Program.cs, выбрав его.

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

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

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

0, 1, 1, 2, 3, 5, 8, 13, 21...

В приведенном ниже примере кода содержится ошибка, поэтому воспользуемся средствами отладки Visual Studio для диагностики и устранения проблемы.

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

Примечание.

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

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

  2. Прежде чем перейти к отладке, давайте изучим работу обновленного кода. Запустите программу, нажав зеленую кнопку пуска в верхней части Visual Studio.

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

Окно терминала с измененными выходными данными программы.

В нашем примере программа должна вычислить значение пятого элемента последовательности Фибоначчи:

0, 1, 1, 2, 3, 5, 8, 13, 21...

В реальной последовательности пятый элемент имеет значение 5, но наша программа вернула значение 3. Давайте воспользуемся отладчиком для диагностики и устранения этой проблемы.

Использование точек останова и пошаговое выполнение

  1. Добавьте точку останова, щелкнув в левом поле строку 1 (int result = Fibonacci(5);).

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

    Снимок экрана: кнопка Шаг с заходом.

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

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

Снимок экрана с панелью

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

    Снимок экрана: кнопка Шаг с обходом.

  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, которое представляет текущее максимальное значение последовательности Фибоначчи. Учитывая это, мы разместим точку останова в строке после присвоения значения sum.

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

    Снимок экрана: установка второй точки останова

    Примечание.

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

  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. Давайте установим еще одну точку останова на строке 18 со следующим кодом:

    return n == 0 ? n1 : n2;
    

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

  7. Удалите ранее заданные точки останова на строках 1 и 13. Щелкните точки останова в поле рядом с номерами строк или снимите флажки точки останова для строк 1 и 13 в области точек останова в левом нижнем левом.

    Снимок экрана: список точек останова на панели точек останова

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

  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. Внесите предыдущее изменение в строку 11 и оставьте точку останова в строке 18.

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

    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.

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