Какой должна быть лучшая стратегия обработки исключений - PullRequest
10 голосов
/ 07 июля 2010

Я работаю над приложением, в котором пользователь вызывает метод из пользовательского интерфейса, для этого я вызываю метод из бизнес-класса, который вызывает другие методы

Интерфейс пользователя -> Метод1 -> Метод2 -> Метод3

Я хочу отобразить сообщение об ошибке для пользователя, если какое-либо исключение происходит в любом из методов.

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

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

Я не хочу использовать соглашение C ++, в котором в качестве результата возвращается целое число.

Ответы [ 7 ]

19 голосов
/ 07 июля 2010

Если вы не можете восстановить из исключения в методе, где происходит исключение, не пытайтесь его перехватить (если только вы не хотите регистрировать его, в этом случае выведите его снова после входа) , Тогда ловите на уровне пользовательского интерфейса.

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

3 голосов
/ 07 июля 2010

Учитывая ваш пример пользовательского интерфейса -> Method1 -> Method2 -> Method3, я бы удостоверился, что пользовательский интерфейс имеет попытку / перехват, но тогда ни один из других методов не должен перехватывать исключения, если они не могут обрабатывать их безповторно метание.И даже если они обрабатывают исключение, вам следует задать вопрос, должно ли это исключение произойти в первую очередь.Если вы обрабатываете исключение и идете своим путем, то это исключение является частью вашего обычного процесса, который вызывает серьезный запах кода.

Вот мои рекомендации:

1) Поставьтекод обработки исключений во всех ваших событиях пользовательского интерфейса, а затем фактическое действие передается другому методу.Не разбрасывайте код обработки исключений повсюду.Это неуклюже и делает код трудным для чтения.

protected void Button1_Click(object sender, EventArgs e) {
   try {
      DoSomething();
   }
   catch Exception e {
      HandleError(e);
   }
}

2) Не делайте этого.Вы потеряете трассировку стека, и следующий разработчик, поддерживающий ваш код, будет выслеживать вас.

try {
   DoSomething();
}
catch Exception e {
   throw e; // don't rethrow!!!
}

Если вы не собираетесь обрабатывать исключение, не перехватывайте его .Или используйте голый бросок так:

try {
   DoSomething();
}
catch SomeException e {
   HandleException(e);
}
catch {
   throw ; // keep my stack trace.
}

3) Бросать исключения только в исключительных случаях.Не включайте блоки try / catch как часть вашего обычного потока процессов.

4) Если вы собираетесь генерировать исключение, не ever генерируйте исключение System.Exception.Получите объект исключения и бросьте его.Я часто просто общий BusinessException класс.Таким образом, пользователи вашего кода могут определить, какие исключения вы создали, а какие связаны с системой / средой.

5) Бросить ArgumentException, ArgumentNullException или ArgumentOutOfRangeException, если вызывающий метод нарушаетконтракт (предварительные условия) вашего метода.Если вы поймаете один из них, это ошибка программирования.Пользователь никогда не должен видеть эти исключения.

Если вы помните, что исключения должны быть очень редким явлением и что обработка их почти всегда связана с пользовательским интерфейсом (поэтому большая часть кода try / catch должна оставаться в пользовательском интерфейсе).уровень) у тебя все получится.

2 голосов
/ 07 июля 2010

Что бы вы ни решили - будьте последовательны . Через 30 дней вы (или другой разработчик) должны будете понять свой код.

Также, как Скотт Хансельман любит цитировать в своем подкасте (и я думаю, что это может быть в книге "Красивый кодекс") -

Все проблемы в информатике могут быть решены с помощью другого уровня косвенности ", - известная цитата, приписываемая Батлеру Лэмпсону, ученому, который в 1972 году представил современный персональный компьютер.

Бросать исключения дорого. И мне нравится, когда у моего бизнес-уровня есть свое специализированное исключение, которое может выдать только он. Таким образом, его легче отследить. Например (вне головы, так как передо мной нет компилятора C #):

public class MyException : Exception
{
   private MyException(Exception ex, string msg) {
       this.Message = msg;
       this.InnerException = ex;
   }

   internal static MyException GetSomethingBadHappened(Exception ex, string someInfo) {
      return new MyException(ex, someInfo);
   }
}
2 голосов
/ 07 июля 2010

Основное правило: «Не перехватывать исключения, когда вы не можете их обработать». Таким образом, любое непредвиденное исключение должно в конечном итоге сказать пользователю, что что-то пошло не так, и закрыть приложение как можно более изящно. Не закрывать приложение рискованно, потому что состояние может быть повреждено и, в свою очередь, поврежденная информация важна для пользователя.

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

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

Если вы хотите регистрировать исключения, вы будете ловить, регистрировать и перебрасывать.

Если вы хотите улучшить сообщение об ошибке, которое вы поймаете, добавьте новое исключение и выбросите. Например, вы можете захотеть обернуть универсальное исключение FileNotFoundException при чтении файла в новом исключении с более описательным сообщением, сообщающим пользователю, какой файл не найден.

1 голос
/ 07 июля 2010

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

1 голос
/ 07 июля 2010

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

public enum AddCustomerResult
{
    Success,
    CustomerAlreadyExists,
    CustomerPreviouslyRetired
}

Вы бы по-прежнему создавали исключения для ошибок, недоступных в базе данных, и т. П .; но вы будете тестировать на ожидаемые (хотя, возможно, редкие) случаи и указывать при необходимости / неудачу / и т. д.

Это всего лишь одна техника, которая хорошо работает для меня.

За исключениями вы хотите выбросить их в исключительных обстоятельствах и в любом месте, где есть обход слоя (мое собственное соглашение, не знаю, насколько оно корректно), вы бы хотели перехватывать исключения и перебрасывать с пользовательским классом-оболочкой для этого слоя.

Например, в DAL вы хотите перехватывать исключения и перебрасывать их как внутреннее исключение в DataAccessException, возможно; в веб-сервисе вы должны обернуть все свои методы в обработчики исключений, чтобы перебросить их с помощью MyWebServiceException. В пользовательском интерфейсе (который перемещается из приложения в экран пользователя) вы хотите поймать, войти в систему и дать им приятное сообщение. Насколько я не вижу причин, чтобы поймать или сбросить в другом месте.

Это дает вам возможность скрыть скрытое исключение (которое вы, вероятно, не хотите показывать вызывающей стороне: например, ошибки базы данных), войти в систему централизованно и предоставить общий повторяемый режим отказа.

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

Надеюсь, это поможет!

0 голосов
/ 07 июля 2010

Практически всегда стоит разделять обработку исключений и отчеты об ошибках.

Поймать исключения, когда их имеет смысл перехватывать, где у вас достаточно контекста, чтобы знать, что именно произошло и как их восстановить - внутри Method1..3. При обнаружении известного исключения отправьте запись в журнал ошибок.

После завершения операции или шага уровень пользовательского интерфейса может проверить журнал ошибок и представить сообщение «Произошли следующие ошибки: ...».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...