Отладка
Простой пример отладочных выводов “на коленке” (более подробно этот вопрос рассматривается в статье Logging with GCC):
#include #define DEBUG int main(){ #ifdef DEBUG fprintf(stderr, "DEBUG: %s:%s:%d: %s\n", __FILE__, __func__, __LINE__, "Debug message"); #endif return 0; }
Почитать подробнее
gdb + cli
GDB имеет достаточно простой, но мощный командный интерфейс и хорошую справку по нему. Однако, если достаточно всего лишь узнать в каком месте программа упала (Segmentation fault), может хватить и следующего набора действий:
Собрать программу с добавлением отладочных данных:
gcc -g myprog.c
Открыть её в отладчике:
gdb ./a.out
Запустить программу командой run:
(gdb) run
Ввести исходные данные, если ваша программа получает какие-то данные на вход. Если требуется перенаправить на вход вашей программе содержимое файла, запустите ее с помощью
(gdb) run input_file.txt
gdb распечатает сообщение “Program received signal SIGSEGV, Segmentation fault.” с указанием строки и инструкции на которой это произошло. Вы также можете после краха Вашей программы написать
(gdb) where
и получить подробный стектрейс
Вывести на экран значения переменных на этот момент можно с помощью команды print
gdb + ide
Любая среда разработки или даже мощный текстовый редактор обычно предоставляют вам графический интерфейс для использования gdb при отладке своих программ. Обычно он достаточно наглядный и имеет хорошее описание для каждой IDE.
Вы можете самостоятельно найти описание использования отладчика в вашей любимой IDE. Для CLion можно посмотреть эти источники:
Отладка в браузере
Давайте отвлечёмся от написания кода и поговорим о его отладке.
Отладка – это процесс поиска и исправления ошибок в скрипте. Все современные браузеры и большинство других сред разработки поддерживают инструменты для отладки – специальный графический интерфейс, который сильно упрощает отладку. Он также позволяет по шагам отследить, что именно происходит в нашем коде.
Мы будем использовать браузер Chrome, так как у него достаточно возможностей, в большинстве других браузеров процесс будет схожим.
Панель «Исходный код» («Sources»)
Версия Chrome, установленная у вас, может выглядеть немного иначе, однако принципиальных отличий не будет.
- Работая в Chrome, откройте тестовую страницу.
- Включите инструменты разработчика, нажав F12 (Mac: Cmd + Opt + I ).
- Щёлкните по панели Sources («исходный код»).
При первом запуске получаем следующее:
Кнопка-переключатель откроет вкладку со списком файлов.
Кликните на неё и выберите hello.js в дереве файлов. Вот что появится:
Интерфейс состоит из трёх зон:
- В зоне File Navigator (панель для навигации файлов) показаны файлы HTML, JavaScript, CSS, включая изображения, используемые на странице. Здесь также могут быть файлы различных расширений Chrome.
- Зона Code Editor (редактор кода) показывает исходный код.
- Наконец, зона JavaScript Debugging (панель отладки JavaScript) отведена для отладки, скоро мы к ней вернёмся.
Чтобы скрыть список ресурсов и освободить экранное место для исходного кода, щёлкните по тому же переключателю .
Консоль
При нажатии на клавишу Esc в нижней части экрана вызывается консоль, где можно вводить команды и выполнять их клавишей Enter .
Результат выполнения инструкций сразу же отображается в консоли.
Например, результатом 1+2 будет 3 , а вызов функции hello(«debugger») ничего не возвращает, так что результатом будет undefined :
Точки останова (breakpoints)
Давайте разберёмся, как работает код нашей тестовой страницы. В файле hello.js щёлкните на номере строки 4 . Да-да, щёлкайте именно по самой цифре, не по коду.
Ура! Вы поставили точку останова. А теперь щёлкните по цифре 8 на восьмой линии.
Вот что в итоге должно получиться (синим это те места, по которым вы должны щёлкнуть):
Точка останова – это участок кода, где отладчик автоматически приостановит исполнение JavaScript.
Пока исполнение поставлено «на паузу», мы можем просмотреть текущие значения переменных, выполнить команды в консоли, другими словами, выполнить отладку кода.
В правой части графического интерфейса мы видим список точек останова. А когда таких точек выставлено много, да ещё и в разных файлах, этот список поможет эффективно ими управлять:
- Быстро перейдите к точке останова в коде (нажав на неё на правой панели).
- Временно отключите точку останова, сняв с неё галочку.
- Удалите точку останова, щёлкнув правой кнопкой мыши и выбрав Remove (Удалить).
- …и так далее.
Условные точки останова
Щелчок правой кнопкой мыши по номеру строки позволяет создать условную точку останова. Она сработает только в тот момент, когда выражение, которое вы должны указать при создании такой точки, истинно.
Это удобно, когда нам нужно остановиться только при определённом значении переменной или для определённых параметров функции.
Команда debugger
Выполнение кода можно также приостановить с помощью команды debugger прямо изнутри самого кода:
function hello(name) < let phrase = `Привет, $!`; debugger; //
Такая команда сработает только если открыты инструменты разработки, иначе браузер ее проигнорирует.
Остановимся и оглядимся
В нашем примере функция hello() вызывается во время загрузки страницы, поэтому для начала отладки (после того, как мы поставили точки останова) проще всего её перезагрузить. Нажмите F5 (Windows, Linux) или Cmd + R (Mac).
Выполнение прервётся на четвёртой строчке (где находится точка останова):
Чтобы понять, что происходит в коде, щёлкните по стрелочкам справа:
- Watch – показывает текущие значения для любых выражений. Вы можете нажать на + и ввести выражение. Отладчик покажет его значение, автоматически пересчитывая его в процессе выполнения.
- Call Stack – показывает цепочку вложенных вызовов. В текущий момент отладчик находится внутри вызова hello() , вызываемого скриптом в index.html (там нет функции, поэтому она называется “анонимной”). Если вы нажмёте на элемент стека (например, «anonymous»), отладчик перейдёт к соответствующему коду, и нам представляется возможность его проанализировать.
- Scope показывает текущие переменные. Local показывает локальные переменные функций, а их значения подсвечены прямо в исходном коде. В Global перечисляются глобальные переменные (то есть вне каких-либо функций). Там также есть ключевое слово this , которое мы ещё не изучали, но скоро изучим.
Пошаговое выполнение скрипта
А теперь давайте пошагаем по нашему скрипту.
Для этого есть кнопки в верхней части правой панели. Давайте рассмотрим их.
– «Resume»: продолжить выполнение, быстрая клавиша F8 .
Возобновляет выполнение кода. Если больше нет точек останова, то выполнение просто продолжается, без контроля отладчиком.
Вот, что мы увидим, кликнув на неё:
Выполнение кода возобновилось, дошло до другой точки останова внутри say() , и отладчик снова приостановил выполнение. Обратите внимание на пункт «Call stack» справа: в списке появился ещё один вызов. Сейчас мы внутри say() .
– «Step»: выполнить следующую команду, быстрая клавиша F9 .
Выполняет следующую инструкцию. Если мы нажмём на неё сейчас, появится alert .
Нажатие на эту кнопку снова и снова приведёт к пошаговому выполнению всех инструкций скрипта одного за другим.
– «Step over»: выполнить следующую команду, но не заходя внутрь функции, быстрая клавиша F10 .
Работает аналогично предыдущей команде «Step», но ведёт себя по-другому, если следующая инструкция является вызовом функции (имеется ввиду: не встроенная, как alert , а объявленная нами функция).
Если сравнить, то команда «Step» переходит во вложенный вызов функцию и приостанавливает выполнение в первой строке, в то время как «Step over» выполняет вызов вложенной функции незаметно для нас, пропуская её внутренний код.
Затем выполнение приостанавливается сразу после вызова функции.
Это хорошо, если нам не интересно видеть, что происходит внутри вызова функции.
– «Step into», быстрая клавиша F11 .
Это похоже на «Step», но ведёт себя по-другому в случае асинхронных вызовов функций. Если вы только начинаете изучать JavaScript, то можете не обращать внимания на разницу, так как у нас ещё нет асинхронных вызовов.
На будущее просто помните, что команда «Step» игнорирует асинхронные действия, такие как setTimeout (вызов функции по расписанию), которые выполняются позже. «Step into» входит в их код, ожидая их, если это необходимо. См. DevTools manual для получения более подробной информации.
– «Step out»: продолжить выполнение до завершения текущей функции, быстрая клавиша Shift + F11 .
Продолжает выполнение и останавливает его в самой последней строке текущей функции. Это удобно, когда мы случайно вошли во вложенный вызов, используя , но это нас не интересует, и мы хотим продолжить его до конца как можно скорее.
– активировать/деактивировать все точки останова(breakpoints).
Эта кнопка не влияет на выполнение кода, она лишь позволяет массово включить/отключить точки останова.
– включить/отключить автоматическую паузу в случае ошибки.
При включении, если открыты инструменты разработчика, ошибка при выполнении скрипта автоматически приостанавливает его. Затем мы можем проанализировать переменные в отладчике, чтобы понять, что пошло не так. Поэтому, если наш скрипт умирает с ошибкой, мы можем открыть отладчик, включить эту опцию и перезагрузить страницу, чтобы увидеть, где он умирает и каков контекст в этот момент.
Continue to here
Щелчок правой кнопкой мыши по строке кода открывает контекстное меню с отличной опцией под названием «Continue to here» («продолжить до этого места»).
Это удобно, когда мы хотим перейти на несколько шагов вперёд к строке, но лень устанавливать точку останова (breakpoint).
Логирование
Чтобы вывести что-то на консоль из нашего кода, существует функция console.log .
Например, это выводит в консоль значения от 0 до 4 :
Как отлаживать маленькие программы
Пусть у вас есть небольшая программа, которая… не работает. Причем не просто как-то не работает, а у вас есть конкретный тест, конкретный пример, на котором она не работает. (Если у вас такого примера нет, то у меня есть отдельный текст про то, что делать в таком случае.) Как понять, что в программе не так, и как это исправить?
На самом деле, на эту тему есть знаменитый текст «How to debug small programs» и его русский перевод, но на мой взгляд рекомендации, приведенные там, — это излишнее усложнение, не нужное на самом деле в 90% действительно простых программ.
Итак, у вас есть тест, но вы не понимаете, почему программа на нем выдает не тот результат, который нужен. Ну, во-первых, по возможности уменьшите тест. Если в вашей задаче вводится какой-то массив или т.п., то не надо разбираться с программой на массиве длины 10. Попробуйте найти тест длины 3-4, на котором программа тоже не будет работать. Если вводится одно число, но дальше будет цикл до этого числа, то подберите число поменьше. И т.п.
Дальше есть несколько подходов.
Представьте себе, что вы компьютер
Основной, самый главный подход, когда программа действительно небольшая, строк 10-20 максимум, состоит в том, чтобы представить себя на месте компьютера и в уме выполнить программу. Возьмите листочек бумажки (или откройте «Блокнот»), выпишите на нем список переменных, которые есть в вашей программе, оставив у каждой переменной место, куда вы будете записывать их значения. Это будет оперативная память вашего компьютера. (В дальнейшем, когда вы освоитесь, бумажка вам не будет нужна, вы будете держать все нужные значения в уме, и все это выполнение будет происходить весьма быстро.)
Далее выполняйте программу пошагово, с самого начала (ну можете пропустить ввод данных, если вы в нем на 200% уверены). При каждом изменении значения каждой переменной выписывая измененное значение на бумажке. Самое важное тут — это подробно и тщательно делать именно то, что написано в программе. Забудьте (точнее лучше задвиньте на задний план) вашу задачу, забудьте, зачем вы писали этот код. Вы работаете за компьютер, компьютер ничего не знает про то, какая у вас задача, он просто тупо выполняет написанный код. Полезно тщательно проговаривать каждую выполняемую операцию. Не забывайте, что операции — это не только присваивания, это еще и все управляющие конструкции (if’ы, циклы и т.д.); не забывайте, что в циклах на каждой итерации выполняются действия, относящиеся собственно к циклу (проверка условия в while, увеличение индекса цикла в for). Все изменения переменных отражайте на бумажке, каждый раз, когда вам нужно значение какой-то переменной, сверяйтесь с бумажкой.
При этом надо где-то глубоко в уме все-таки помнить, какую задачу вы решаете, и какой код зачем написан, чтобы, как только реальное выполнение кода отойдет от того, что вы имели в виду, сразу это и заметить.
Пример. Задача «Переставить элементы в обратном порядке». Типичный код, который тут многие пишут, примерно такой:
# a - массив, который вы считали for i in range(len(a)): t = a[i] a[i] = a[len(a) - i] a[len(a) - i] = t
Вы запускаете программу на тесте 1 2 3 , и она падает. Хорошо, давайте представим, что мы выполняем эту программу за компьютер. У нас есть массив a , в котором записано 1 2 3 (и это записано у нас на бумажке), и переменные i и t . Поехали.
Начинается цикл for , переменная i становится равна 0 (на бумажке рядом с именем переменной i пишем 0 ).
Команда t = a[i] . Чему у нас равно i ? Смотрим на бумажку, i равно 0 . Чему равно a[i] ? Смотрим на бумажку, a[i] это a[0] это 1 . Это значение записывается в t . Записываем на бумажке рядом с t единицу.
Команда a[i] = a[len(a)-i] . Чему у нас равно i ? Нулю. Чему равно len(a) ? Трем. Чему равно len(a)-i ? Трем. Чему равно a[3] ? Ой, выход за пределы массива (не забываем, что элементы в массиве нумеруются с нуля; полезно на бумажке под значениями элементов массива подписать их индексы).
Вот собственно мы нашли первую ошибку. Обратите внимание, что мы специально тщательно и подробно все проговаривали; если бы вы действовали поверхностно, то вы могли бы сразу сказать: « a[len(a)-i] — это последний элемент массива (ведь я именно для этого писал этот код), поэтому это просто 3 ». И вы не заметили бы ошибку. Именно поэтому и надо по максимуму забыть, что обозначает этот код, а вместо этого просто четко и подробно выполнять то, что написано, постоянно сверяясь с бумажкой.
Хорошо, давайте исправим ошибку, теперь код такой:
# a - массив, который вы считали for i in range(len(a)): t = a[i] a[i] = a[len(a) - i - 1] a[len(a) - i - 1] = t
Запускаем программу — и она выдает 1 2 3 , т.е. как будто массив не изменился. Это все равно неправильно, поэтому поехали еще раз.
Начинается цикл for , переменная i становится равна 0 (на бумажке рядом с именем переменной i пишем 0 ).
Команда t = a[i] . Чему у нас равно i ? Смотрим на бумажку, i равно 0 . Чему равно a[i] ? Смотрим на бумажку, a[i] это a[0] это 1 . Это значение записывается в t . Записываем на бумажке рядом с t единицу.
Команда a[i] = a[len(a)-i-1] . Чему у нас равно i ? Нулю. Чему равно len(a) ? Трем. Чему равно len(a)-i-1 ? Двум. Чему равно a[2] ? Трем. Это значение записывается в a[i] . Что такое a[i] ? У нас i равно 0 , поэтому это нулевой элемент массива. Зачеркиваем единичку, которая записана на бумажке на нулевом месте в массиве, записываем туда 3.
Команда a[len(a) - i - 1] = t . Чему у нас равно t ? Единице. Что такое a[len(a) - i - 1] ? Выражение в квадратных скобках мы только что считали (но надо как минимум внимательно проверить, что выражение тут написано то же, а лучше пересчитать), поэтому это a[2] . Значит, в a[2] записываем 1. Зачеркиваем число 3, которое раньше было написано в a[2] , записываем туда 1.
Продолжаем. Итерация цикла закончилась, начинается новая итерация цикла. i становится равно 1 .
Продолжаем все делать так же тщательно и подробно. Я не буду дальше все это расписывать, но (особенно если вы еще не видите ошибки в коде выше) можете продолжить и все-таки найти ошибку.
Добавьте отладочный вывод
Второй полезный подход — добавить в программу вывод на экран промежуточных значений переменных в ключевых местах программы. Тут надо суметь понять, что такое «ключевые места» и какие переменные выводить. Обычно, например, если у вас в программе есть какие-то циклы, то полезно добавить вывод как минимум в конце каждой итерации, и выводить те переменные, которые меняются в цикле, в том числе для циклов for — переменную цикла. Если у вас сложная конструкция из if’ов, то добавить вывод внутрь каждого if’а, чтобы видеть, в какой именно if зашла программа. Если у вас несколько функций или тем более рекурсия, то добавить отладочный вывод типа «вошли в такую-то функцию» (зачастую с указанием параметров функции) и «вышли из такой-то функции». Если у вас просто сложные вычисления, длинная формула или несколько формул — то добавить вывод промежуточных результатов вычислений; возможно, для этого придется длинную формулу разбить на части (и это заодно сделает ее понятнее).
После этого запускаете программу на том тесте, на котором она не работает, смотрите на отладочный вывод, проверяете все значения и находите первое значение, которое неверно. Таким образом вы довольно хорошо локализуете проблему, вы будете знать, что ошибка, например, в вычислении той или иной переменной. Дальше уже действуйте по ситуации: если программа не очень сложная, то эта переменная меняется в одном-двух местах, и вы сразу можете посмотреть, как она считается и почему значение получается неправильным. Тем более что вы, наверное, вывели на экран не только значение этой переменной, но и значения других переменных, от которых она зависит, поэтому сразу можете подставить эти значения в формулу и проверить.
Если же программа более сложная, эта переменная меняется в нескольких местах, или вы вывели на экран недостаточно информации, чтобы легко расследовать проблему, то добавьте еще отладочного вывода, чтобы более подробно видеть, откуда взялось это значение. И повторите.
Может быть и другая причина проблемы — не неправильное значение переменной, а, например, заход не в тот if или слишком раннее или слишком позднее окончание цикла, и т.д. Но в любом случае, как только вы добавили отладочный вывод, вы стали намного лучше понимать, что происходит в программе, и вам будет намного легче найти ошибку.
Рассмотрим в качестве примера тот же код, который мы разбирали выше. Добавим в него отладочный вывод примерно такой:
# a - массив, который вы считали for i in range(len(a)): t = a[i] a[i] = a[len(a) - i - 1] a[len(a) - i - 1] = t print("i=", i, "a выполните-программу-пошагово-в-среде-разработки">Выполните программу пошагово в среде разработки
Многие среды разработки позволяют вам выполнить код пошагово. По сути, это аналог первого указанного выше способа, но всю работу за вас делает компьютер, вам остается только многократно нажимать на одну и ту же кнопку. Это, безусловно, полезный во многих случаях способ, но на самом деле не стоит им злоупотреблять. В частности, когда речь идет про действительно небольшую программу, то использование пошаговой отладки зачастую оказывается более сложным и долгим, чем отладка любым из описанных выше способов, особенно когда вы уже освоили выполнение программы в уме.
Поговорите с уточкой
Еще одна стандартная рекомендация — это взять резиновую уточку (ну на самом деле любую игрушку, или даже можно и без игрушки), и подробно по порядку объяснить ей, что делает ваша программа, что делает в ней каждая строчка и почему всё написано так, а не иначе.
Вот прямо так и говорите: мне надо переставить элементы массива в обратном порядке. Для этого мне надо взять первый элемент массива и поменять местами с последним, потом второй с предпоследним и так далее. Как я это пишу в программе? Мне надо менять много пар элементов, поэтому я делаю цикл for . В цикле for переменная i обозначает номер элемента, который я буду менять местами с симметричным. Она должна меняться от 0 до… и вот тут вы понимаете, где ошибка в программе.
Это весьма полезный прием, если вы сумеете его освоить. Ключевой момент, что тут надо делать — надо подробно и про каждый элемент кода объяснить, зачем вы это делаете и почему код написан именно так. В частности, в примере выше фраза «она должна меняться от 0 до…» возникла потому, что вы видите, что в коде написано range(len(a)) , значит, надо объяснить уточке, почему написано именно так.
Важно все тщательно и подробно проговаривать; точно так же, как и при выполнении программы в уме вы должны последовательно выполнять все действия, а не «срезать углы» словами «а, это будет последний элемент массива», так и здесь не надо «срезать углы», пропуская объяснения тех или иных моментов и полагая, что они понятны и очевидны.
Помимо собственно поиска ошибок, метод уточки еще помогает вам обнаружить и осознать места в программе, которые вы не до конца понимаете, и самим в них подробнее разобраться (это близко и связано с поиском ошибок, но бывает полезно и в других ситуациях). Но про это и вообще про осознание кода я, пожалуй, напишу когда-нибудь еще один пост.
Мой курс по алгоритмическому программированию (и подготовке к олимпиадам) для школьников, студентов и всех желающих — algoprog.ru
Методы и средства отладки, помогающие писать более качественный код
Область применения:Visual Studio Visual Studio для Mac
Visual Studio Code ![]()
Исправление ошибок и ошибок в коде может занять много времени и иногда страстить задачу. Требуется время, чтобы узнать, как эффективно выполнять отладку. Мощная интегрированная среда разработки, например Visual Studio, может упростить работу. Интегрированная среда разработки помогает устранять ошибки и выполнять отладку кода быстрее, а также помогает создавать более удобный код с меньшим количеством ошибок. В этой статье представлено целостное представление процесса исправления ошибок, поэтому вы можете знать, когда следует использовать анализатор кода, когда использовать отладчик, как исправить исключения и как кодировать для намерения. Если вы уже знаете, что вам нужно использовать отладчик, ознакомьтесь с отладчиком.
В этой статье вы узнаете, как работать с интегрированной среду разработки, чтобы сделать сеансы программирования более продуктивными. Мы затронем несколько задач, таких как:
- Подготовка кода к отладке с помощью анализатора кода интегрированной среды разработки
- Исправление исключений (ошибки времени выполнения)
- Минимизация ошибок путем написания кода с учетом его предназначения (с помощью оператора assert)
- Аспекты использования отладчика
Чтобы продемонстрировать эти задачи, мы показываем несколько наиболее распространенных типов ошибок и ошибок, которые могут возникнуть при попытке отладки приложений. Хотя пример кода — C#, концептуальные сведения обычно применимы к C++, Visual Basic, JavaScript и другим языкам, поддерживаемым Visual Studio (за исключением отмеченных). На снимках экрана представлены примеры на C#.
Создание примера приложения с некоторыми ошибками
В следующем коде есть ошибки, которые можно исправить с помощью интегрированной среды разработки Visual Studio. Это простое приложение, которое имитирует получение данных JSON из некоторых операций, десериализация данных в объект и обновление простого списка с новыми данными.
Чтобы создать приложение, необходимо установить Visual Studio и рабочую нагрузку разработки для классических приложений .NET.
- Установите Visual Studio бесплатно со страницы скачиваемых материалов Visual Studio, если еще не сделали этого.
- Если необходимо установить рабочую нагрузку, но у вас уже есть Visual Studio, выберите "Сервис >получения инструментов" и "Компоненты". Запускается Visual Studio Installer. Выберите рабочую нагрузку Разработка классических приложений .NET и нажмите Изменить.
Выполните следующие действия, чтобы создать приложение:
- Откройте Visual Studio. В окне запуска выберите Создание нового проекта.
- В поле поиска введите консоль , а затем один из вариантов консольного приложения для .NET.
- Выберите Далее.
- Введите имя проекта, например Console_Parse_JSON, и нажмите кнопку "Далее" или "Создать", как применимо. Выберите рекомендуемую целевую платформу или .NET 8, а затем нажмите кнопку "Создать". Если вы не видите шаблон проекта консольного приложения для .NET, перейдите к разделу "Сервис >получения инструментов и компонентов", который открывает Установщик Visual Studio. Выберите рабочую нагрузку Разработка классических приложений .NET и нажмите Изменить. Visual Studio создаст консольный проект и откроет его в Обозревателе решений в области справа.
Когда проект готов, замените код по умолчанию в файле Program.cs проекта следующим примером кода:
using System; using System.Collections.Generic; using System.Runtime.Serialization.Json; using System.Runtime.Serialization; using System.IO; namespace Console_Parse_JSON < class Program < static void Main(string[] args) < var localDB = LoadRecords(); string data = GetJsonData(); User[] users = ReadToObject(data); UpdateRecords(localDB, users); for (int i = 0; i < users.Length; i++) < Listresult = localDB.FindAll(delegate (User u) < return u.lastname == users[i].lastname; >); foreach (var item in result) < Console.WriteLine($"Matching Record, got name=, lastname=, age="); > > Console.ReadKey(); > // Deserialize a JSON stream to a User object. public static User[] ReadToObject(string json) < User deserializedUser = new User(); User[] users = < >; MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)); DataContractJsonSerializer ser = new DataContractJsonSerializer(users.GetType()); users = ser.ReadObject(ms) as User[]; ms.Close(); return users; > // Simulated operation that returns JSON data. public static string GetJsonData() < string str = "[< \"points\":4o,\"firstname\":\"Fred\",\"lastname\":\"Smith\">,]"; return str; > public static List LoadRecords() < var db = new List< >; User user1 = new User(); user1.firstname = "Joe"; user1.lastname = "Smith"; user1.totalpoints = 41; db.Add(user1); User user2 = new User(); user2.firstname = "Pete"; user2.lastname = "Peterson"; user2.totalpoints = 30; db.Add(user2); return db; > public static void UpdateRecords(List db, User[] users) < bool existingUser = false; for (int i = 0; i < users.Length; i++) < foreach (var item in db) < if (item.lastname == users[i].lastname && item.firstname == users[i].firstname) < existingUser = true; item.totalpoints += users[i].points; >> if (existingUser == false) < User user = new User(); user.firstname = users[i].firstname; user.lastname = users[i].lastname; user.totalpoints = users[i].points; db.Add(user); >> > > [DataContract] internal class User < [DataMember] internal string firstname; [DataMember] internal string lastname; [DataMember] // internal double points; internal string points; [DataMember] internal int totalpoints; >>
Ищите красные и зеленые волнистые линии!
Прежде чем пытаться запустить пример приложения и отладчик, проверьте код в редакторе кода на наличие красных и зеленых волнистых линий. Они представляют ошибки и предупреждения, определенные анализатором кода интегрированной среды разработки. Красные волнистые линии обозначают ошибки времени компиляции, которые необходимо исправить, прежде чем можно будет запустить код. Зеленые волнистые линии обозначают предупреждения. Хотя часто приложение можно запустить без исправления предупреждений, они могут быть источником ошибок, а их устранение во многих случаях помогает сэкономить время и силы. Эти предупреждения и ошибки также можно просмотреть в окне Список ошибок, если вы предпочитаете представление списка.
В примере приложения вы увидите несколько красных волнистых волн, которые необходимо исправить, и зеленый, который необходимо исследовать. Вот первая ошибка.

Чтобы устранить эту ошибку, можно посмотреть на другую функцию интегрированной среды разработки, представленную значком лампочки.
Проверяйте лампочку!
Первая красная волнистая линия представляет ошибку времени компиляции. Наведите на нее указатель мыши, и появится сообщение The name `Encoding` does not exist in the current context .
Обратите внимание, что в нижнем левом углу от этой ошибки отображается значок лампочки. Наряду с значком отвертки значок
лампочки представляет быстрые действия, которые помогут вам исправить или рефакторинг кода. Лампочка представляет проблемы, которые необходимо исправить. Отвертка указывает на проблемы, которые при желании можно исправить. Используйте первое предлагаемое исправление для устранения этой ошибки, щелкнув элемент using System.Text слева.

При выборе этого элемента Visual Studio добавляет using System.Text инструкцию в верхней части файла Program.cs , а красная волнистая полоса исчезает. (Если вы не уверены в изменениях, примененных предлагаемым исправлением, выберите Предварительная версия ссылки на изменения справа перед применением исправления.)
Описанная выше ошибка является распространенной и обычно устраняется путем добавления в код нового оператора using . Существует несколько распространенных ошибок, таких как The type or namespace "Name" cannot be found. эти типы ошибок, могут указывать на отсутствующую ссылку на сборку (щелкните проект правой кнопкой мыши, выберите "Добавить> ссылку"), имя с ошибкой или недостающую библиотеку, которую необходимо добавить (для C#, щелкните проект правой кнопкой мыши и выберите "Управление пакетами NuGet").
Исправление оставшихся ошибок и предупреждений
В этом коде есть еще несколько волнистых линий. Здесь присутствует типичная ошибка преобразования типа. При наведении указателя мыши на волнистую линию вы увидите, что код пытается преобразовать строку в int, которая не поддерживается, если только не добавить явный код для преобразования.

Так как анализатор кода не может угадать ваше намерение, в данном случае лампочки, которые могут помочь вам, отсутствуют. Чтобы устранить эту ошибку, необходимо знать предназначение кода. В этом примере не слишком трудно увидеть, что points должно быть числовым (целочисленным) значением, так как вы пытаетесь добавить points в totalpoints .
Чтобы устранить эту ошибку, измените член points класса User с такого:
[DataMember] internal string points;
[DataMember] internal int points;
Красные волнистые линии в редакторе кода исчезают.
Затем наведите указатель мыши на зеленую волнистую линию в объявлении элемента данных points . Анализатор кода сообщает, что переменной никогда не присваивается значение.

Как правило, это указывает на проблему, которую необходимо исправить. Однако в этом примере приложения вы фактически сохраняете данные в переменной points в процессе десериализации, а затем добавляете это значение в элемент данных totalpoints . В этом примере вы знаете предназначение кода и можете спокойно проигнорировать предупреждение. Однако если вы хотите устранить это предупреждение, можно заменить этот код:
item.totalpoints = users[i].points;
item.points = users[i].points; item.totalpoints += users[i].points;
Зеленая волнистая линия исчезает.
Исправление исключения
Когда вы исправили все красные волнистые линии и разрешенные или по крайней мере расследованные- все зеленые волнистые волнистые линии, вы готовы запустить отладчик и запустить приложение.

Нажмите клавишу F5 (Отладка > запуска отладки) или кнопку "Начать отладку" на панели инструментов отладки.
На этом этапе пример приложения выдает исключение SerializationException (ошибка времени выполнения). То есть приложение перехватывает данные, которые он пытается сериализовать. Так как приложение запущено в режиме отладки (подключен отладчик), помощник по исправлению ошибок отладчика переносит вас прямо к коду, выдавшему исключение, и выдает полезное сообщение об ошибке.

Сообщение об ошибке указывает, что значение 4o не может быть проанализировано как целое число. Итак, в этом примере вы знаете, что данные указаны неправильно: 4o вместо 40 . Однако если вы не управляете данными в реальном сценарии (предположим, что вы получаете его из веб-службы), что вы делаете с этим? Как это исправить?
При возникновении исключения необходимо задать себе пару вопросов:
- Является ли это исключение простой ошибкой, которую можно исправить? или
- Может ли это исключение возникнуть у пользователей?
В первом случае исправьте ошибку. (В примере приложения необходимо исправить плохие данные.) Если это последний вариант, может потребоваться обработать исключение в коде с помощью try/catch блока (мы рассмотрим другие возможные стратегии в следующем разделе). В примере приложения замените приведенный ниже код:
users = ser.ReadObject(ms) as User[];
try < users = ser.ReadObject(ms) as User[]; >catch (SerializationException) < Console.WriteLine("Give user some info or instructions, if necessary"); // Take appropriate action for your app >
Блок try/catch подразумевает некоторое снижение производительности, поэтому их следует использовать только тогда, когда это действительно необходимо, то есть, когда (а) они могут возникнуть в окончательной версии приложения и (б) документация по методу указывает, что необходимо проверить наличие исключения (с предположением, что документация завершена). Во многих случаях можно соответствующим образом обработать исключение, чтобы пользователь никогда с ним не столкнулся.
Ниже приведено несколько важных советов по обработке исключений:
- Избегайте использования пустого блока перехвата, например catch (Exception) <> , который не принимает соответствующее действие для предоставления или обработки ошибки. Пустой или неинформативный блок перехвата может скрыть исключения и сделать код более сложным для отладки, а не проще.
- Используйте блок try/catch вокруг конкретной функции, которая вызывает исключение ( ReadObject в примере приложения). Если использовать его вокруг большого фрагмента кода, вы в итоге скроете расположение ошибки. Например, не используйте блок try/catch вокруг вызова родительской функции ReadToObject , как показано здесь, или вы не сможете точно определить, где возникло исключение.
// Don't do this try < User[] users = ReadToObject(data); >catch (SerializationException)
Для данного примера приложения исправьте SerializationException в методе GetJsonData , изменив 4o на 40 .
Уточнение предназначения кода с помощью оператора assert

Нажмите кнопку "Перезапустить" на панели инструментов отладки (CTRL + SHIFT + F5). Это приведет к перезапуску приложения за меньшее количество шагов. В окне консоли вы увидите приведенные ниже выходные данные.

Вы можете увидеть что-то в этом выходных данных неправильно. Значения имени и фамилии для третьей записи пусты!
Это подходящий момент, чтобы рассмотреть полезный и часто недооцененный подход к написанию кода, который заключается в использовании операторов assert в функциях. Добавив следующий код, вы включаете среду выполнения проверка, чтобы убедиться, что firstname и lastname нет null . Замените приведенный ниже код в методе UpdateRecords :
if (existingUser == false) < User user = new User(); user.firstname = users[i].firstname; user.lastname = users[i].lastname;
// Also, add a using statement for System.Diagnostics at the start of the file. Debug.Assert(users[i].firstname != null); Debug.Assert(users[i].lastname != null); if (existingUser == false) < User user = new User(); user.firstname = users[i].firstname; user.lastname = users[i].lastname;
Добавив такие операторы assert в функции в процессе разработки, можно указать предназначение кода. В предыдущем примере мы укажем следующие элементы:
- Для имени требуется допустимая строка.
- Для фамилии требуется допустимая строка.
Указав таким образом намерение, вы применяете необходимые требования. Это простой и удобный способ, который можно использовать для изучения ошибок во время разработки. (Операторы assert также используются в качестве главного элемента в модульных тестах.)

Нажмите кнопку "Перезапустить" на панели инструментов отладки (CTRL + SHIFT + F5).
Код assert активен только в отладочной сборке.
При перезапуске отладчик приостанавливает выполнение на операторе assert , так как выражение users[i].firstname != null вычисляется как false вместо true .

Ошибка assert указывает на проблему, которую необходимо изучить. assert может охватывать множество сценариев, в которых исключение отображается не всегда. В этом примере пользователь не видит исключения, а null значение добавляется как firstname в списке записей. Это условие может привести к проблемам позже (например, в выходных данных консоли) и может быть труднее выполнить отладку.
В сценариях, где вы вызываете метод для значения null , возникает NullReferenceException . Обычно следует избегать использования блока try/catch для общего исключения, то есть исключения, не привязанного к определенной функции библиотеки. Любой объект может вызывать NullReferenceException . Если вы не уверены, изучите документацию по функции библиотеки.
Во время процесса отладки рекомендуется использовать определенный оператор assert , пока не станет понятно, что его нужно заменить реальным исправлением кода. Допустим, вы решили, что пользователь может столкнуться с исключением в сборке выпуска приложения. В этом случае необходимо выполнить рефакторинг кода, чтобы убедиться, что приложение не выдает неустранимое исключение или не приводит к возникновению какой-либо другой ошибки. Итак, чтобы исправить этот код, замените приведенный ниже код:
if (existingUser == false) < User user = new User();
if (existingUser == false && users[i].firstname != null && users[i].lastname != null) < User user = new User();
Используя этот код, вы выполняете требования к коду и убедитесь, что запись с firstname или lastname значением null не добавляется в данные.
В этом примере мы добавили в цикл два оператора assert . Как правило, при использовании assert рекомендуется добавлять операторы assert в точке входа (начале) функции или метода. В настоящее время вы просматриваете UpdateRecords метод в примере приложения. В этом методе вы можете столкнуться с проблемами, если какой-либо из его аргументов равен null , поэтому проверьте их оба с помощью оператора assert в точке входа функции.
public static void UpdateRecords(List db, User[] users) < Debug.Assert(db != null); Debug.Assert(users != null);
Для предыдущих операторов ваше намерение заключается в том, чтобы загрузить существующие данные ( db ) и извлечь новые данные ( users ) перед обновлением.
Оператор assert можно использовать с любым видом выражения, которое разрешается в true или false . Например, можно добавить оператор assert следующего вида.
Debug.Assert(users[0].points > 0);
Приведенный выше код полезен, если необходимо указать следующее намерение: для обновления записи пользователя требуется новое значение точки больше нуля (0).
Проверка кода в отладчике
Итак, теперь, когда вы устранили все критические проблемы с примером приложения, можно двигаться дальше.
Мы рассмотрели помощник по исправлению ошибок отладчика, но отладчик является гораздо более функциональным инструментом, который позволяет совершать и другие действия, такие как пошаговое выполнение кода и изучение его переменных. Эти более мощные возможности полезны во многих сценариях, особенно в следующих сценариях:
- Вы пытаетесь изолировать ошибку среды выполнения в коде, но не можете сделать это с помощью методов и инструментов, которые ранее обсуждались.
- Вы хотите проверить код, то есть смотреть его во время его выполнения, чтобы убедиться, что он ведет себя так, как вы ожидаете и делаете то, что вы хотите. Это поучительно смотреть код во время его выполнения. Так вы можете получить дополнительные сведения о коде и выявить ошибки до того, как проявляются их очевидные симптомы.
Дополнительные сведения об использовании функций отладчика см. в статье Отладка для начинающих.
исправлять проблемы производительности;
Ошибки другого типа включают неэффективный код, из-за которого приложение выполняется медленнее или использует слишком много памяти. Как правило, оптимизация производительности осуществляется на более позднем этапе разработки приложения. Однако вы можете столкнуться с проблемами с производительностью раньше (например, вы видите, что некоторые части приложения работают медленно), и вам может потребоваться протестировать приложение с помощью средств профилирования на ранних этапах. Дополнительные сведения о средствах профилирования, таких как средство загрузки ЦП и анализатор памяти, см. в разделе Знакомство со средствами профилирования.
Следующие шаги
В этой статье вы узнали, как предотвращать и устранять многие распространенные ошибки в коде и когда использовать отладчик. Далее вы можете подробнее узнать об использовании отладчика Visual Studio для исправления ошибок.
