Всегда ли плохой опыт, чтобы поймать System.Exception?

Пожалуйста, рассмотрите следующий фрагмент кода, который выдает три разных исключения (а именно System.Configuration.ConfigurationErrorsException, System.FormatException и System.OverflowException):

int SomeInt = Convert.ToInt32(ConfigurationManager.AppSettings["SomeIntValue"]);

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

try
{
    int SomeInt = ConfigurationManager.AppSettings["SomeIntValue"];
}
catch (Exception ThisException)
{
    /* Log and display error message. */
}

или я должен использовать три блока catch и повторять код внутри каждого из них?

Ответ 1

Я не думаю, что это плохая практика. Если ваша желаемая функциональность "всякий раз, когда этот код генерирует исключение, тогда выполняйте эти действия", то я думаю, что catching System.Exception вполне подходит.

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

Ответ 2

См. С# Exception Handling Fall Through для обсуждения этой проблемы.

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

Обновление (и немного вне области действия)

Также есть несколько раз, я думаю, что это действительно так, чтобы все поймать. Это очень редко, но иногда в веб-службах, или если вы что-то делаете в фоновом потоке asp.net, а также исключение, перезапустите все приложение, и люди могут потерять сессию, если вы зависнули от этого.

Ответ 3

Плохая практика - поймать System.Exception., или еще лучше, плохой практикой является обращение с System.Exception где угодно, но верхний уровень вашего приложения. Что вам нужно сделать:

  • Catch System.Exception
  • Проверить исключение для типов, которые вы планируете обрабатывать одинаково
  • Повторите, если это не один из них.

Пример кода:

catch (Exception ex)
{
    if (ex is FormatException || ex is OverflowException || ex is ConfigurationErrorsException) {
        CommonHandler();
    }
    else {
        throw;
    }
}

Ответ 4

Это не обязательно плохая практика, плохая практика обычно возникает, когда вы делаете глупые вещи в блоке catch. Захват базового класса исключений - хорошая мера безопасности, предполагающая, что вам нужно что-то делать, когда возникает ошибка. Случай для улавливания определенного класса исключений лучше всего проявляется, когда этот конкретный класс дает вам информацию, на которую вы можете действовать, например, catching SqlException может помочь вам взглянуть на определенные свойства, специфичные для этого класса, и позволить вам действовать по этой проблеме.

Часто люди поймают исключение и делают что-то глупое, как создать новое исключение или, что еще хуже, усвоить детали исключения.

Часто пропущенный паттерн состоит в том, что вы можете поймать, совершить ретронирование.

catch(Exception ex)
{
 //do something
 throw;
}

таким образом сохраняя детали исключения.

Ответ 5

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

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

В общем, мышление таково: Нужно ли мне предпринимать разные действия в отношении разных видов исключений, которые выбрасываете? Чаще всего нет ответа, нет, поэтому пустой catch(Exception ex){ /* log... */ } (или даже нет catch вообще, если исключение всегда является фатальным).

Изменить: пустое, как в пустой проверке, а не пустой блок:)

Ответ 6

Что действительно нужно, но иерархия исключений .net не предоставляет, является чистым способом выделения исключений, которые означают: "Запрошенная операция не выполнялась, но состояние системы по существу прекрасное, за исключением того, что подразумевается операция не произошла" из тех, которые означают "CPU горит, и даже попытка сохранить текущую работу пользователя, скорее всего, не ухудшит ситуацию". Существует множество контекстов, в которых нужно действительно попытаться поймать все исключения первого типа, в то время как в идеале не поймать их во втором. Хотя есть несколько градаций за пределами двух выше, как правило, при перехвате исключений на самом деле не важно различие между InvalidArgumentException или InvalidOperationException; что нужно заботиться о том, является ли общее состояние системы действительным или поврежденным.

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

Кстати, если бы у меня были мои барабанщики, был бы класс ExceptionBase, из которого будут получены все исключения; большинство исключений вытекает из Exception (которое, в свою очередь, выводится из ExceptionBase), но такие вещи, как ThreadAbortException, StackOverflowException, OutOfMemoryException и т.д., будут получены из CriticalException. Таким образом, можно было бы уловить большинство "неожиданных" исключений, не случайно задушив действительно плохих.