Ответ
Правильная обработка исключений — это не просто try-catch, а стратегия, которая делает код надежным и удобным для отладки.
1. Базовый синтаксис и рекомендации:
try
{
// Код, который может выбросить исключение
var data = File.ReadAllText("config.json");
var config = JsonSerializer.Deserialize<Config>(data);
}
catch (FileNotFoundException ex) // Ловите конкретные исключения
{
_logger.LogWarning(ex, "Файл конфигурации не найден, используются значения по умолчанию.");
// Восстановление: создаем конфиг по умолчанию
config = Config.Default;
}
catch (JsonException ex) // Обработка ошибок десериализации
{
_logger.LogError(ex, "Неверный формат JSON в конфигурационном файле.");
// Невосстановимая ошибка — пробрасываем выше
throw new InvalidOperationException("Ошибка загрузки конфигурации", ex);
}
finally
{
// Код для освобождения ресурсов (файлов, сетевых подключений)
// Выполняется ВСЕГДА, даже если было исключение или return.
}
2. Ключевые принципы:
- Не ловите то, что не можете обработать: Пусть исключение всплывает до уровня, где есть достаточно контекста для его корректоной обработки (например, контроллер API вернет
500 Internal Server Error). - Используйте конкретные типы исключений в
catch:catch (Exception)должен быть крайней мерой, обычно на верхнем уровне приложения (например, в middleware). - Всегда логируйте исключения: Используйте
ILogger, передавая само исключение как параметр, чтобы сохранить стектрейс. - Избегайте «пустых» catch-блоков (
catch {}): Это скрывает ошибки и усложняет отладку.
3. Создание пользовательских исключений: Создавайте их для ошибок предметной области (business logic).
public class InsufficientFundsException : Exception
{
public decimal CurrentBalance { get; }
public decimal RequiredAmount { get; }
public InsufficientFundsException(decimal currentBalance, decimal requiredAmount)
: base($"Недостаточно средств. Текущий баланс: {currentBalance}, требуется: {requiredAmount}")
{
CurrentBalance = currentBalance;
RequiredAmount = requiredAmount;
}
}
// Использование:
throw new InsufficientFundsException(balance, amount);
4. Глобальная обработка в ASP.NET Core: Используйте middleware для централизованной обработки.
// В Program.cs
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
// Логируем исключение
_logger.LogError(exception, "Необработанное исключение");
// Возвращаем клиенту безопасный ответ (без деталей в продакшене)
await context.Response.WriteAsJsonAsync(new { error = "Произошла внутренняя ошибка." });
});
});
5. Исключения и асинхронный код:
В асинхронных методах исключения «заворачиваются» в AggregateException при использовании Task.Wait() или Task.Result. Всегда используйте await, который пробрасывает исходное исключение.
// ПЛОХО: Получите AggregateException
Task.Run(() => throw new InvalidOperationException()).Wait();
// ХОРОШО: Получите InvalidOperationException
await Task.Run(() => throw new InvalidOperationException()); Ответ 18+ 🔞
А, обработка исключений! Ну это ж классика, вечная тема, как эти ваши крестьяне в Палестинах. Все думают: «О, try-catch, ебать, я сейчас как обложусь блоками — и код станет надёжным!» А потом получается такая же история, как с Герасимом и Муму — вроде всё правильно сделал, а в итоге всё равно топит собаку в озере, потому что нихуя не понял, что происходит.
Смотри, вот тебе база, без которой вообще никуда.
1. Ну, синтаксис-то простой, блядь.
try
{
// Код, который может накосячить
var data = File.ReadAllText("config.json");
var config = JsonSerializer.Deserialize<Config>(data);
}
catch (FileNotFoundException ex) // Лови конкретное говно, а не всё подряд!
{
_logger.LogWarning(ex, "Файл конфигурации не найден, используются значения по умолчанию.");
// Восстановился — молодца
config = Config.Default;
}
catch (JsonException ex) // О, а вот это уже серьёзнее
{
_logger.LogError(ex, "Неверный формат JSON в конфигурационном файле.");
// Тут уже нихуя не поправишь — пусть летит выше, пусть начальство разбирается
throw new InvalidOperationException("Ошибка загрузки конфигурации", ex);
}
finally
{
// А это святое. Этот кусок выполнится ВСЕГДА. Даже если всё ебнулось к чертям.
// Закрывай файлы, рви соединения, делай что должен.
}
2. Главные принципы, которые нарушают все, а потом плачут.
- Не лови то, что не можешь переварить. Ну серьёзно, зачем тебе ловить
OutOfMemoryExceptionв методе расчета скидки? Пусть летит наверх, там, в контроллере, его поймают и отдадут клиенту внятную ошибку500, а не просто сдохнет поток. - Лови по имени, а не просто
Exception.catch (Exception)— это как кричать «Муму!» на всё, что движется. Это для самого верхнего уровня, где ты уже просто логируешь и говоришь «всё пропало». - Логируй, сука, всегда! И передавай само исключение в логгер, а не просто строку. Иначе потом будешь как дурак смотреть в логи и гадать, откуда ноги растут.
- Пустой
catch {}— это прямой путь в ад. Это когда ты взял и закопал ошибку в землю. Через полгода продакшн падает, а ты нихуя не понимаешь почему. Пиздец.
3. Свои исключения — для своих косяков. Допустим, у тебя логика перевода денег. «Недостаточно средств» — это же не системная ошибка, а бизнес-правило. Вот и создай своё.
public class InsufficientFundsException : Exception
{
public decimal CurrentBalance { get; }
public decimal RequiredAmount { get; }
public InsufficientFundsException(decimal currentBalance, decimal requiredAmount)
: base($"Недостаточно средств. Текущий баланс: {currentBalance}, требуется: {requiredAmount}")
{
CurrentBalance = currentBalance;
RequiredAmount = requiredAmount;
}
}
// А кидаешь так, с чувством:
throw new InsufficientFundsException(balance, amount);
4. В ASP.NET Core — делай по-взрослому.
Не размазывай try-catch по каждому контроллеру. Сделай один жирный middleware, который всех поймает, залогирует и даст клиенту внятный ответ.
// В Program.cs
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
// Вот тут ты его и ловишь, гада. Логируешь со всем стеком.
_logger.LogError(exception, "Необработанное исключение");
// А клиенту — только общую формулировку, без подробностей, кто где и на что наступил.
await context.Response.WriteAsJsonAsync(new { error = "Произошла внутренняя ошибка." });
});
});
5. Асинхронщина — отдельная песня, ебать. Тут главное — не наступать на классические грабли.
// ПЛОХО, НЕ ДЕЛАЙ ТАК: Ты получишь AggregateException, и будешь потом как Герасим мычать, пытаясь докопаться до настоящей причины.
Task.Run(() => throw new InvalidOperationException()).Wait();
// ХОРОШО, ДЕЛАЙ ТАК: await развернёт тебе оригинальное исключение, как есть. Красота.
await Task.Run(() => throw new InvalidOperationException());
Короче, смысл в чём: исключения — это не враги, это система оповещения. Твоя задача — не заткнуть все сирены скотчем, а правильно на них реагировать. Одни — просто предупреждения, на другие — надо нажать большую красную кнопку. Главное — понимать, что происходит, а не просто бездумно обкладываться try-catch, как одеялом. Иначе будет тебе та же трагедия — придётся свою же «Муму» топить, потому что вовремя не разобрался.