Исключение исключений в С# om nom nom

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

Исключение исключений:

try
{
  … do something meaningful
}
catch(SomeException ex)
{
   // eat exception
}

Ответ 1

try
{
 ...
}
catch(SomeException e)
{
 //Do whatever is needed with e
 throw; //This rethrows and preserves call stack.
}

Ответ 2

Ловить и обрабатывать определенные типы исключений. Хорошая практика заключается в не, просто поймайте System.Exception. Устойчивая процедура будет сильно печатать исключения, которые она знает, как обращаться.

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

В зависимости от конкретного типа вы можете или не захотите реконструировать его. Например, исключение синтаксического анализа ASP, выведенное на страницу с ошибкой, которая ИСПОЛЬЗУЕТ код, вызывающий исключение, вызовет бесконечный цикл.

try
{

}
catch( FileIOException )
{
    // unwind and re-throw as determined by the specific exception type
}
catch( UnauthorizedAccessException )
{
    // unwind and re-throw as determined by the specific exception type
}
catch( SomeOtherException )
{
    // unwind and re-throw as determined by the specific exception type
}
catch( Exception )
{
   // log and re-throw...add your own message, capture the call stack, etc.

   // throw original exception
   throw;

   // OR, throw your own custom exception that provides more specific detail and captures
   // the original exception as the inner exception
   throw new MyStronglyTypedException();
}
finally
{
     // always clean up
}

Ответ 3

Большинство людей считают это совершенно злым, чтобы есть/подавлять исключения, особенно с уловками. (По иронии судьбы, они используют уловку всего отклика "не используйте уловки, это зло":-). Я не понимаю религиозного пыла, с которым люди попугают эту точку зрения, потому что, если использовать разумно, в этом подходе нет ничего плохого.

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

  • Многие люди, похоже, забывают, что catch alls может часто обрабатывать любые исключения правильно, даже если они не знают подробностей о том, что такое исключение. Под этим я подразумеваю, что они могут гарантировать, что состояние программы остается стабильным, и программа продолжает работать в рамках своих параметров проектирования. Или могут быть даже побочные эффекты, такие как поиск пользователем кнопки, но они все равно не потеряют какие-либо данные (то есть грациозная деградация лучше, чем фатальный сбой). Например, иногда вы хотите вернуть одно значение при успешном завершении и по умолчанию, если по какой-либо причине вы отказались. Часть кода разработки - это знать, когда сообщать об ошибках пользователю и когда исправлять проблему от их имени, чтобы их программа "просто работала". В этой ситуации хорошо разработанный catch-all часто является правильным инструментом для работы.

  • Исключения беспокоят меня. В принципе, исключение - это гарантированный сбой программы, если я не справляюсь с этим. Если я добавлю только обработку исключений для исключений, которые я ожидаю, моя программа по своей сути является хрупкой. Подумайте, как легко его можно сломать:

    • Если программист забывает документировать одно исключение, которое они могут выбросить, я не буду знать, что мне нужно его поймать, а мой код будет иметь уязвимость, о которой я не знаю.
    • Если кто-то обновляет метод, чтобы он выдавал новый тип исключения, это новое исключение могло бы пульсировать стек вызовов до тех пор, пока он не ударит по моему коду. Но мой код не был создан для обработки исключения. Не говорите мне, что библиотеки, которые я звоню, никогда не изменятся.
    • Каждый тип исключения, который вы конкретно обрабатываете, является другим кодом для проверки. Это значительно увеличивает сложность тестирования и/или риски, которые могут быть незаметными для поврежденного кода обработки.
  • Представление, лежащее в основе представления "подавление зла", состоит в том, что все исключения представляют собой нестабильность или ошибку, но во многих случаях программисты используют исключения, чтобы вернуть немного больше, чем информацию о состоянии. Например, FileNotFound. Программист, записывающий код ввода-вывода файла, решил от моего имени, что отсутствующий файл является фатальной ошибкой. И может быть. Это зависит от меня, чтобы поймать это и решить, что на самом деле это обычная и совершенно нормальная или ожидаемая ситуация. Много времени, подавляя исключения, необходимо просто прекратить кому-то другое "решение", изъявающее мою заявку. Старый подход простого игнорирования кодов возврата ошибок не всегда был плохим, особенно учитывая объем усилий, необходимых для улавливания и подавления множества "статусных" исключений, которые связаны с ними.

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

Ответ 4

Все зависит от того, где живет код.

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

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

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

… do something meaningful

Ответ 5

Ваш код можно переписать (есть исключение), например,

try
{
  … do something meaningful
}
catch
{
   // eat exception
}

Но я не понимаю, что вы хотите сделать, рефакторинг!

Edit:

Re-throwing с использованием throw; не работает всегда. Прочтите это → http://weblogs.asp.net/fmarguerie/archive/2008/01/02/rethrowing-exceptions-and-preserving-the-full-call-stack-trace.aspx

Ответ 6

Чтобы добавить замечательные комментарии, уже предоставленные.

Существует три способа "повторного выброса" исключения:

catch (Exception ex)
{
   throw;
}

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

catch (Exception ex)
{
   throw ex;
}

Вышеприведенная исходная цепочка исключений и начинается новая.

catch (Exception ex)
{
   throw new MyException("blah", ex);
}

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

Ответ 7

В общем, не рекомендуется поймать общий Exception, если вы не сможете его обработать. Я думаю, что правильный ответ - это сочетание ответов Тима и Джошуа. Если есть особые исключения, которые вы можете обрабатывать и оставаться в хорошем состоянии, например FileNotFoundException, вы должны поймать его, обработать и перейти дальше, как показано здесь:

try
{
    // do something meaningful
}
catch(FileNotFoundException)
{
    MessageBox.Show("The file does not exist.");
}

Если вы не можете справиться с этим и оставаться в хорошем состоянии, не поймите его в первую очередь.

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

try
{
    BeginTransaction();

    // do something meaningful

    CommitTransaction();
}
catch(FileNotFoundException)
{
    MessageBox.Show("The file does not exist.");
    AbortTransaction();
}
catch(Exception)
{
    AbortTransaction();
    throw;                  // using "throw;" instead of "throw ex;" preserves
                            // the stack trace
}

Ответ 8

Скомпонуйте его:

// No try
{ 
   … do something meaningful 
} 
// No catch

и исключение обрабатывается в основном цикле.

Ответ 9

если блок catch() только восстанавливает исключение и не выполняет никакой реальной обработки исключений, вам вообще не нужно try..catch.

Ответ 10

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

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

Ответ 11

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

При этом, если исключение стоит проинформировать пользователя о (например, протоколировании), тогда для этого обязательно используйте блок catch.

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

Ответ 12

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

catch(Exception e)
{
  throw;
}

Зачем вам это нужно? Пример использования: Где-то в вашем приложении у вас есть сторонний код, и вы его завершаете, и если он генерирует исключения, вы бросаете WrappingException.

Когда вы выполняете какой-либо другой код, вы можете получить исключение либо из 3rdparty, либо из своего собственного, поэтому вам может понадобиться:

try
{
//code that runs 3rd party
//your code, but it may throw Null ref or any other exception
}
catch( WrappingException)
{
 throw;
}
catch( Exception e)
{
 throw new MyAppLayer3Exception("there was exception...", e);
}

В этом случае вы не обертываете WrappingException с помощью исключения MyAppLayer3.

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

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

Ответ 13

Особый способ употребления исключений не имеет значения. Никогда не употребляйте исключения никакими способами!

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

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

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

Ответ 14

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

try
{
 .....
}
catch (something)
{
  // we can safely ignore ex becuase ....
}

Ответ 15

Иногда, просто лучше не иметь дело с исключениями, если вы действительно не хотите иметь дело с дополнительной ответственностью, которая приходит с исключениями. Например, вместо того, чтобы ловить NullReferenceException, почему бы просто не убедиться, что объект существует, прежде чем пытаться что-то сделать с ним?

if (yourObject != null)
{
    ... do something meaningful with yourObject ...
}

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

Я занимаюсь обработкой исключений в течение последних 15 лет, начиная с первых шести версий Delphi, вплоть до (и в том числе).NET 1.0-4.0. Это мощный инструмент, но это инструмент, который часто используется. Я нашел последовательно, за это время наиболее эффективный процесс обработки исключений откладывается до if-then до try-catch.

Ответ 16

Одна из основных проблем с иерархией исключений заключается в том, что исключения классифицируются на основе того, что произошло, а не на основе состояния системы. Некоторые исключения означают, что "функция не могла выполнить свою задачу, но она не нарушала состояние системы". Другие означают: "Беги за свою жизнь! Вся система тает!" Во многих случаях это было бы вполне правильно для рутины, которая могла бы справиться с отказом вызываемого метода проглатывать любые исключения и исключения из прежнего типа; в других случаях такие исключения должны быть отвергнуты таким образом, который указывает на возможное нарушение состояния (например, из-за сбоя в операции, необходимой для состояния системы reset), хотя попытка выполнить эту операцию ничего не мешала, тот факт, что состояние не было reset означает, что оно повреждено).

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