Мой технический руководитель настаивает на этом механизме исключения:
try
{
DoSth();
}
catch (OurException)
{
throw;
}
catch (Exception ex)
{
Util.Log(ex.Message, "1242"); // 1242 is unique to this catch block
throw new OurException(ex);
}
1242 - это идентификатор метода catch, которым мы обрабатываем исключение, отличное от OurException. Каждый блок catch в проекте должен иметь уникальный идентификатор, чтобы мы могли узнать, где произошло исключение, просмотрев журналы.
Для каждого метода мы должны поймать OurException и выбросить его. Если выброшено исключение другого типа, мы должны зарегистрировать его и замаскировать с помощью OurException перед повторной генерацией.
Это разумный подход? Если есть, то какие альтернативы лучше?
Редактировать: Мне сказали, что трассировка стека не дает значимых результатов в режиме выпуска. Вы предлагаете перехват и выброс общих исключений в порядке?
Edit2: Всем спасибо. Я использовал ваши ответы как часть своего аргумента против этого, но мне сказали, что вы недостаточно опытны и не знаете, как действовать в реальных жизненных ситуациях. Я должен идти этим путем.
Да, это .net. OurException - наш единственный настраиваемый класс исключений.
Как убедиться, что идентификаторы уникальны? Почему бы просто не записать трассировку стека?
Я не понимаю, почему вы поймаете OurException, ничего не сделаете с ним, а затем повторно выбросите. Помоги мне...
Я думаю, это для того, чтобы не запускать часть ведения журнала, когда уже зарегистрированное исключение перехвачено (мы предполагаем, что каждое OurException регистрируется немедленно).





Я думаю, что иметь жестко закодированный номер в строке выброса - не лучшая практика, как узнать, используется ли этот номер или нет? Или какой следующий номер ошибки нужно выбросить? Может быть, вы можете, по крайней мере, указать в перечислении ... не уверен.
Все выглядит правильно, кроме этого странного числа.
Трассировка стека будет содержать необходимую информацию.
Я бы подумал, что использование трассировки стека будет намного более интуитивно понятным, чем любой идентификатор.
Что касается пользовательского исключения, почему бы этого не сделать?
try
{
DoSth();
}
catch(Exception ex)
{
Util.Log(ex.Message, ex.StackTrace);
if (ex is OurException) throw ex;
else throw new OurException(ex); // use the original exception as the InnerException
}
Кроме того, я не уверен, почему вы хотите повторно генерировать исключение после того, как оно было обработано - вы можете объяснить причину этого?
@Ali A - Очень верный момент, поэтому позвольте мне перефразировать - зачем повторно генерировать исключение вместо того, чтобы закончить его обработку прямо здесь?
Обновлено:
Почему бы не сделать это вместо повторного заброса?
try
{
DoSth();
}
catch(Exception ex)
{
Util.HandleException(ex);
}
Util.HandleException:
public static void HandleException(ex)
{
Util.Log(ex); // Util.Log should log the message and stack trace of the passed exception as well as any InnerExceptions - remember, than can be several nested InnerExceptions.
// Any additional handling logic, such as exiting the application, or showing the user an error message (or redirecting in a web app)
}
Таким образом, вы знаете, что каждое исключение регистрируется только один раз, и вы не бросаете их обратно в дикую природу, чтобы нанести дополнительный ущерб.
На мой взгляд, это не было "обработано", это было зарегистрировано.
Вы также можете посмотреть Блок приложения для обработки исключений.
Я использовал его в нескольких проектах, и он очень полезен. Особенно, если позже вы захотите изменить способ работы обработки исключений и какую информацию собирать.
Регистрация трассировки стека была бы намного лучше, чем жестко запрограммированное число. Число ничего не говорит вам о том, как эта процедура называлась.
Кроме того, с числом трудно управлять, и при этом он подвержен ошибкам.
edit: это правда, что stacktrace не дает всегда значимых результатов в режиме выпуска, но я думаю, что то же самое можно сказать об этой жестко запрограммированной схеме номеров! :-)
Предоставляет ли компилятор для используемого вами языка макросы с текущим именем файла и номером строки? Это было бы лучше, чем пытаться самостоятельно набрать уникальное число. И, если вы не можете сделать это автоматически, сделайте это вручную или придумайте какую-нибудь схему, гарантирующую уникальность, которая не требует центрального распределения номеров!
Мне кажется, что трассировка стека - лучший способ справиться с этим. Что произойдет, если вы по ошибке повторно воспользуетесь номером?
Что касается того, хорошая это идея или нет, при отсутствии какой-либо дополнительной информации я бы предпочел сказать нет. Если каждый вызов метода заключен в блок try / catch, это будет очень дорого. Как правило, исключения можно разделить на два лагеря: те, с которыми вы можете разумно ожидать и справиться, и те, которые действительно являются ошибками (или, по крайней мере, не ожидаются успешно). Мне кажется, что использование дробовика для перехвата всех исключений может сделать больше для сокрытия последних, чем для их разрешения. Это будет особенно верно, если на верхнем уровне вы перехватили все исключения, так что единственная запись, которая у вас есть, - это сообщение журнала.
У меня есть два типа исключений: восстанавливаемое исключение и фатальное исключение. Если какой-либо объект вызывает устраняемое исключение, это означает, что произошла ошибка, но объект не поврежден и может быть использован снова. Если какой-либо объект вызывает фатальное исключение, это означает, что состояние объекта повреждено, и любая попытка использования объекта приведет к новой ошибке.
Обновлять:: все исключения могут быть обработаны как можно скорее и как можно ближе к источнику ошибки. Например, если объект, хранящийся в коллекции, выдает фатальное исключение, обработчик исключений просто удаляет этот объект из коллекции и удаляет его, а не всю коллекцию объектов.
Я думаю, что должно быть еще несколько типов, но в целом такая схема кажется гораздо более полезной, чем иерархия исключений, основанная на том, какая операция завершилась неудачей. Если подпрограмма вызывает метод LoadDocument и генерирует исключение ArrayIndexException, какие выводы следует сделать в отношении оставшейся части состояния системы?
Я не понимаю, насколько это полезно. Вы уже можете узнать, откуда взялось исключение, с помощью трассировки стека, и вы добавляете ненужный уровень сложности.
Недостатки:
Это число не внушает особого доверия, трассировка стека намного лучше
Зачем нужны 2 блока catch, если ваше настраиваемое исключение можно обработать на "catch (Exception ex)" "?
Потому что, если это как OurException, оно уже зарегистрировано, поэтому нам не нужно снова регистрировать и переносить его.
Иметь OurException довольно странно. Обычно вам нужны специализированные блоки catch, а затем последний, тот, который улавливает общий Exception, - это то место, где вы ведете журнал:
try
{
DoSomething();
}
catch (DivideByZeroException)
{
// handle it here, maybe rethrow it, whatever
}
// more catch blocks
catch (Exception)
{
// oops, this is unexpected, so lets log it
}
Но то, что вы делаете, будет работать. Я действительно считаю, что 1242 должен уйти. Вот способ распечатать метод, имя файла и номер строки, которые вы могли бы использовать вместо этого. Сам не пробовал, но выглядит неплохо:
[Conditional("DEBUG")]
public static void DebugPrintTrace()
{
StackTrace st = new StackTrace(true);
StackFrame sf = st.GetFrame(1); // this gets the caller's frame, not this one
Console.WriteLine("Trace "
+ sf.GetMethod().Name + " "
+ sf.GetFileName() + ":"
+ sf.GetFileLineNumber() + "\n");
}
Я думаю, что это плохой шаблон, потому что у вас есть информация обо всем стеке вызовов, легко доступная в исключении, нет необходимости псевдо-обрабатывать исключение, регистрируя и повторно генерируя его. Ловите исключение только в том месте, где вы действительно можете что-то сделать с проблемой.
Насколько я понимаю, цель исключений - распространение неожиданных ошибок. Если вы поймаете его достаточно близко к методу, который его выбрасывает, вы больше понимаете, как с ним обращаться.
Ваш пример - поймать неожиданную ошибку и повторно выбросить ее вверх по цепочке. Это не обработка исключения, а перенос его в собственное.
Но ваша упаковка, похоже, не добавляет больше значения исключению, но может даже загромождать вещи.
Крайний пример:
void a() {
try {
c();
} catch(MyException1 ex) {
throw;
} catch(Exception ex) {
log(ex);
throw new MyException1(ex);
}
}
void b() {
try {
a();
} catch(MyException2 ex) {
throw;
} catch(Exception ex) {
log(ex);
throw new MyException2(ex);
}
}
Обратите внимание, что в предыдущем примере исходное исключение регистрируется дважды. И это связано с двумя исключениями. Когда вы регистрируете одно и то же исключение несколько раз, его становится труднее отследить (поскольку файл журнала становится довольно большим).
Конечно, это может быть крайний пример, но мне трудно поверить, что все ваше приложение использует только один тип исключения. Он недостаточно описывает все возможные типы ошибок.
Лично я предпочитаю регистрировать исключение только в блоке catch, где я его обрабатываю. Где-нибудь еще может просто создать дублированное ведение журнала.
Поскольку мне разрешено вводить только одно настраиваемое исключение (MyException в вашем примере), мне нужно написать второй метод, используя MyException вместо MyException2. Это гарантирует, что исключения регистрируются один раз. Все остальное, что вы говорите, правильно, и да, именно этого они и хотят от меня.
Мое приложение построено в режиме выпуска, и я использую блок приложения для обработки исключений, поэтому у меня никогда не было блоков кода catch ex, он перехватывается EHAB, он сам записывает в файл журнала всю необходимую информацию; трассировка стека и т. д., время и т. д.
единственная проблема с этим, если кто-то поставил
catch ex
{
//do stuff
throw ex;
}
так как это сотрет трассировку стека.
Здравствуйте! Вы можете создать собственный класс Exception, чтобы ведение журнала происходило в конструкторе исключений и вместо идентификатора использовалась трассировка стека. Это .NET? Colby Africa