Сами по себе нет никаких технических проблем, связанных со статическим методом обработки исключений / повторных исключений, однако с точки зрения передового опыта наличие единственного метода, который волшебным образом «обрабатывает» исключения, производит впечатление потенциального запаха кода.Исключения по своему названию являются исключительными, каждый отдельный случай требует тщательной проработки, чтобы убедиться, что вы делаете правильные вещи, и поэтому я считаю маловероятным, что ваш HandleException
метод всегда будет делать что-то разумное.
В качестве крайнего примера я знаю одно такое приложение, в котором почти каждый отдельный метод был обернут в блок try-catch с вызовом статического метода-обработчика исключений, который генерировал общий тип MyApplicationException
.Это очень плохая идея:
- Это загромождает код
- Это усложняет понимание трассировки стека
- Это очень затрудняет для вызывающих абонентовловить и обрабатывать определенные типы исключений
- Это делает исключение еще более значительным, чем раньше
Моим любимым был метод, который не был реализован и который выглядел примерно так:
void SomeException()
{
try
{
throw new NotImplementedException();
}
catch(Exception ex)
{
throw ExceptionHandler.HandleException(...);
}
}
Хуже всего, конечно, то, что он совершенно бессмысленный .Как я уже говорил, исключения являются исключительными - каждый блок try ... catch
требует тщательного обдумывания и рассмотрения того, как он должен вести себя, использование общего метода HandleException
является сигналом немедленного предупреждения, что, вероятно, это не так.
Возврат исключений
В общем случае исключать исключение следует только в двух случаях:
- Если вы хотите добавить контекстную информацию к исключению (например, имятекущего обрабатываемого файла)
Когда вам нужно было перехватить исключение, чтобы обработать какой-то конкретный случай, например, обработка ошибки «недостаточно места на диске»
catch (IOException ex)
{
long win32ErrorCode = Marshal.GetHRForException(ex) & 0xFFFF;
if (win32ErrorCode == ERROR_HANDLE_DISK_FULL || win32ErrorCode == ERROR_DISK_FULL)
{
// Specific "out of disk space" error handling code
}
else
{
throw;
}
}
«Пузырьки» (то есть перехват и перебрасывание исключения без каких-либо действий с ним) совершенно не нужны - это то, что исключения уже разработаны, чтобы делать все сами!
Обработка исключений
Другие люди говорили, что "исключения должны обрабатываться там, где это имеет смысл", и я даже дал этоСам, но, оглядываясь назад, не думаю, что это особенно полезный совет!:)
Под этим обычно подразумевают, что вы должны обрабатывать исключения по особым причинам , и что вам следует выбирать, где в вашем приложении обрабатывать это исключение в зависимости от этой причины.
Например, если вы хотите отобразить сообщение об ошибке, информирующее пользователя о том, что у него нет разрешения на изменение файла, если вы получаете сообщение об ошибке отказа в доступе, возможно, в вашем коде пользовательского интерфейса есть определенный блок try-catchчто делает это:
catch (IOException ex)
{
long win32ErrorCode = Marshal.GetHRForException(ex) & 0xFFFF;
if (win32ErrorCode == ERROR_ACCESS_DENIED)
{
// Display "access denied error"
}
else
{
throw;
}
}
Обратите внимание, что это очень специфично для этого случая, который мы хотим обработать - он перехватывает только тот конкретный тип исключения, в котором заинтересованы , а выполняет дополнительные проверкичтобы отфильтровать до интересующего нас конкретного случая.
В качестве альтернативы, если вы хотите регистрировать необработанные ошибки или корректно отображать сообщения об ошибках для пользователя вместо экрана ошибок IIS 505, то место для этого либо вGlobal.asax или через пользовательскую страницу ошибок - ASP.Net Страницы пользовательских ошибок
Моя точка зрения заключается в том, что при обработке исключений мы тщательно обдумываем, чего мы хотим достичь с точки зрения функциональности приложения (например, логики повторов, сообщений об ошибках, ведения журнала и т. Д.) И реализации нашей логики обработки исключений.чтобы конкретно удовлетворить эти требования самым целенаправленным способом - не существует волшебной структуры обработки исключений и нет стандартного кода.
Избегайте исключений полностью, когда это возможно!
Я обычно нахожу, что лучшая стратегия - просто избегать исключений, когда это возможно! Например, если ваша страница анализирует вводимые пользователем числа, и вы хотите отображать сообщения проверки, когда они вводят глупые значения, то проверяйте ввод заранее, вместо того, чтобы перехватывать исключения:
Bad:
void DoSomething()
{
int age = int.Parse(ageTextBox.Text);
if (age < 0)
{
throw new ArgumentOutOfRangeException("age must be positive");
}
if (age >= 1000)
{
throw new ArgumentOutOfRangeException("age must be less than 1000");
}
}
void Button_Click(object sender, EventArgs e)
{
try
{
DoSomething();
}
catch (Exception ex)
{
DisplayError(ex.Message);
}
}
Хорошо:
void Button_Click(object sender, EventArgs e)
{
int age;
if (!int.TryParse(ageTextBox.Text, out age))
{
DisplayError("Invalid age entered");
}
if (age < 0)
{
DisplayError("age must be positive");
}
if (age >= 1000)
{
DisplayError("age must be less than 1000");
}
DoSomething();
}
Пользователи постоянно вводят неверные данные - обработка это действительно логика приложения и не должна попадать в реальность обработки исключений - это, безусловно, не событие, которое я бы назвал «исключительным».
Конечно, это не всегда возможно, однако я считаю, что использование этой стратегии упрощает код и облегчает следование логике приложения.