Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
Исправление ошибок в коде может занять много времени и иногда быть разочаровывающей задачей. Требуется время, чтобы узнать, как эффективно выполнять отладку. Мощная интегрированная среда разработки, например 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 запускается. Выберите компонент разработки настольных приложений .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++)
{
List<User> result = localDB.FindAll(delegate (User u) {
return u.lastname == users[i].lastname;
});
foreach (var item in result)
{
Console.WriteLine($"Matching Record, got name={item.firstname}, lastname={item.lastname}, age={item.totalpoints}");
}
}
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\"},{\"lastName\":\"Jackson\"}]";
return str;
}
public static List<User> LoadRecords()
{
var db = new List<User> { };
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<User> 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 имеет некоторые затраты на производительность, поэтому вам нужно использовать их только в тех случаях, когда они действительно нужны, т. е. где (a) они могут возникнуть в версии выпуска приложения, и где (b) документация для метода указывает, что следует проверить исключение (если документация завершена!). Во многих случаях вы можете обрабатывать исключение соответствующим образом, и пользователь никогда не должен знать об этом.
Ниже приведены несколько важных советов по обработке исключений:
Избегайте использования пустого блока перехвата, например
catch (Exception) {}, который не принимает соответствующие меры для обнаружения или обработки ошибки. Пустой или неинформативный блок перехвата может скрыть исключения и сделать код более сложным для отладки, вместо упрощения.try/catchИспользуйте блок вокруг конкретной функции, которая вызывает исключение (ReadObjectв примере приложения). Если вы используете его вокруг более крупного фрагмента кода, вы в конечном итоге скрываете расположение ошибки. Например, не используйтеtry/catchблок вокруг вызова родительской функцииReadToObject, показанной здесь, или вы не будете точно знать, где произошло исключение.// Don't do this try { User[] users = ReadToObject(data); } catch (SerializationException) { }Для незнакомых функций, которые вы включаете в приложение, особенно функции, взаимодействующие с внешними данными (например, веб-запрос), проверьте документацию, чтобы узнать, какие исключения функция, скорее всего, вызовет. Это может быть важная информация для правильной обработки ошибок и отладки приложения.
Для примерного приложения исправьте SerializationException в методе GetJsonData, изменив 4o на 40.
Подсказка
Если у вас есть Copilot, вы можете получить помощь с использованием искусственного интеллекта при отладке исключений. Просто найдите кнопку "Попросить Copilot"
. Дополнительные сведения см. в разделе Отладка с помощью Copilot.
Проясните намерение кода, используя 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<User> db, User[] users)
{
Debug.Assert(db != null);
Debug.Assert(users != null);
Для предыдущих инструкций вы намерены загрузить существующие данные () и получить новые данные (dbusers) перед обновлением.
Вы можете использовать assert с любым выражением, которое сводится к true или false. Например, можно добавить выражение assert, как это.
Debug.Assert(users[0].points > 0);
Приведенный выше код полезен, если вы хотите указать следующее намерение: для обновления записи пользователя требуется новое значение точки больше нуля (0).
Проверка кода в отладчике
Теперь, когда вы исправили все критически важное, что неправильно с примером приложения, вы можете перейти к другим важным вещам!
Мы показали вспомогательный инструмент отладчика, но отладчик является гораздо более мощным инструментом, который также позволяет выполнять другие действия, такие как пошаговое выполнение кода и проверка его переменных. Эти более мощные возможности полезны во многих сценариях, особенно в следующих сценариях:
Вы пытаетесь изолировать ошибку среды выполнения в коде, но не можете сделать это с помощью методов и инструментов, которые ранее обсуждались.
Вы хотите проверить код, то есть наблюдать за ним во время его выполнения, чтобы убедиться, что он ведет себя так, как вы ожидаете, и выполняет то, что вы хотите.
Поучительно наблюдать за кодом во время его выполнения. Вы можете узнать больше о коде таким образом и часто выявлять ошибки, прежде чем они проявляют любые очевидные симптомы.
Чтобы узнать, как использовать основные функции отладчика, см. статью "Отладка для абсолютных начинающих".
Устранение проблем с производительностью
Ошибки другого типа включают неэффективный код, который приводит к медленному выполнению приложения или использованию слишком большого объема памяти. Как правило, оптимизация производительности — это то, что вы делаете позже в разработке приложений. Однако вы можете столкнуться с проблемами с производительностью раньше (например, вы видите, что некоторые части приложения работают медленно), и вам может потребоваться протестировать приложение с помощью средств профилирования на ранних этапах. Дополнительные сведения о средствах профилирования, таких как средство использования ЦП и анализатор памяти, см. в статье Сначала ознакомьтесь с инструментами профилирования.
Связанный контент
В этой статье вы узнали, как избежать и устранить множество распространенных ошибок в коде и когда использовать отладчик. Далее узнайте больше об использовании отладчика Visual Studio для устранения ошибок.