Общая стратегия обработки исключений для .NET - PullRequest
12 голосов
/ 26 июня 2009

Я привык иметь блоки try / catch в каждом методе. Причина этого в том, что я могу поймать каждое исключение в точке нарушения и записать его. Из моего чтения и разговоров с другими я понимаю, что это не популярное мнение. Нужно только поймать то, с чем вы готовы справиться. Тем не менее, если я не поймаю в момент нарушения, было бы возможно никогда не регистрировать это нарушение и не знать об этом. Примечание: когда я ловлю и не справляюсь, я все равно бросаю. Это позволяет мне разрешить исключению распространяться на что-то, что с ним справится, и в то же время позволяет регистрировать его в точке нарушения.

Итак ... Как можно избежать попытки / перехвата в каждом методе, но при этом регистрировать ошибку в том месте, где она произошла?

Ответы [ 11 ]

18 голосов
/ 26 июня 2009

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

Это означает, например, что вы должны окружить код обработчика событий блоком try / catch. Обработчик события может быть «вершиной стека». То же самое для обработчика ThreadStart или обратного вызова из асинхронного метода.

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

В случае ASP.NET вы можете разрешить ASP.NET Health Monitoring регистрировать исключение для вас.

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

9 голосов
/ 26 июня 2009

Вы можете видеть все в трассировке стека - не нужно пытаться / ловить каждый метод.

Придерживайтесь нескольких правил:

  1. Используйте try / catch, только если вы хотите использовать пользовательский тип исключения
  2. Определите новый тип исключения, только если верхние уровни должны знать, что
  3. Попробуйте / поймайте на верхнем уровне вместо того, чтобы делать это для каждого метода
4 голосов
/ 27 июня 2009

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

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

Сказки трассировки стека и сообщения "отказано в разрешении" может быть достаточно, чтобы вы как программист могли выяснить, что пошло не так, но моя цель - предоставить пользователю значимую информацию, такую как «Не удалось открыть файл« C: \ lockedfile.txt ». В доступе отказано».

Как в:

private void DoSomethingWithFile(string filename)
{
    // Note: try/catch doesn't need to surround the whole method...

    if (File.Exists(filename))
    {
        try
        {
            // do something involving the file
        }
        catch (Exception ex)
        {
            throw new ApplicationException(string.Format("Cannot do something with file '{0}'.", filename), ex);
        }
    }
}

Я также хотел бы упомянуть, что даже люди, говорящие «только одна попытка / отловить», по-видимому, все равно будут использовать попытку / окончание в своем коде, поскольку это единственный способ гарантировать правильную очистку и т. Д.

1 голос
/ 27 июня 2009

Я бы хотел использовать ELMAH для обработки исключений, что в значительной степени является понятием «пусть происходят исключения». ELMAH позаботится о том, чтобы регистрировать их, и вы даже можете настроить его на отправку электронной почты, когда исключения для определенного проекта достигают или превышают определенный порог. В моем отделе мы остаемся как можно дальше от блоков try / catch. Если что-то не так в приложении, мы хотим сразу узнать, в чем проблема, чтобы исправить ее, вместо того, чтобы подавлять исключение и обрабатывать его в коде.

Если происходит исключение, это означает, что что-то не так. Идея состоит в том, чтобы заставить ваше приложение делать только то, что должно. Если он делает что-то другое и вызывает исключения, ваш ответ должен состоять в том, чтобы установить причину, по которой это происходит, а не в том, чтобы это происходило, и обрабатывать это в коде. Это просто моя / наша философия, и это не для всех. Но все мы слишком часто горели из-за того, что приложение «по какой-то причине» съело исключение, и никто не знает, что что-то не так.

И никогда, никогда, никогда не ловите общее исключение. Всегда, всегда, всегда перехватывайте самое конкретное исключение, так что если выдается исключение, но это не тот тип, который вы ожидаете, опять же, вы будете знать, потому что приложение аварийно завершится. Если вы просто перехватываете (Exception e), то независимо от того, какой тип исключения выдается, ваш блок catch теперь будет отвечать за реагирование на каждый тип исключения, который может быть сгенерирован. А если это не так, то вы сталкиваетесь с целым «едящим» исключением, когда что-то идет не так, но вы никогда не узнаете, пока не станет слишком поздно.

1 голос
/ 26 июня 2009

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

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

Конечно, просто повторяем, что всплывающие окна с вашими исключениями и ведение журнала трассировки стека лучше, чем вложение блоков try-catch-log-throw по всему вашему коду.

1 голос
/ 26 июня 2009

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

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

0 голосов
/ 27 июня 2009

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

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

0 голосов
/ 27 июня 2009

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

Обработка исключений не должна быть запоздалой мыслью. Убедитесь, что вы делаете это последовательно. Я видел, как много людей ставили широкие попытки / улов от начала до конца каждого метода и ловили общее исключение. Люди думают, что это помогает им получить больше информации, хотя на самом деле это не так. Больше в некоторых случаях меньше, так как меньше значит больше. Я никогда не устаю от аксиомы, что «Следует использовать исключения, чтобы отметить исключительное поведение». Восстановите, если можете, и постарайтесь уменьшить количество общих исключений. Нет ничего более расстраивающего, когда вы пытаетесь решить проблему и видите сотни таких же исключений NullReferenceException или аналогичных, когда что-то идет не так.

0 голосов
/ 26 июня 2009

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

1) Чтобы предотвратить сбой программы и потерю информации пользователями, я делаю это

                runProgram:
                try
                {
                    container.ShowDialog();
                }
                catch (Exception ex)
                {
                    ExceptionManager.Publish(ex);
                    if (MessageBox.Show("A fatal error has occurred.  Please save work and restart program.  Would you like to try to continue?", "Fatal Error", MessageBoxButtons.YesNo) == DialogResult.Yes)
                        goto runProgram;
                    container.Close();
                }
Контейнер

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

2) Я ловлю исключения только в тех методах, где, как я ожидаю, что-то может пойти не так (например, тайм-аут).

3) Для удобства чтения, если у вас есть блок try catch с кучей кода в секции try и кучей в секции catch, лучше извлечь этот код в метод с хорошо известным именем.

   public void delete(Page page) 
   {
      try 
      {
         deletePageAndAllReferences(page)
      }
      catch (Exception e) 
      {
         logError(e);
      }
   }
0 голосов
/ 26 июня 2009
  1. Поймать и выбросить исключение, которое вы не можете обработать, - не более чем трата процессорного времени. Если вы не можете ничего сделать с исключением или с этим исключением, проигнорируйте его и позвольте вызывающему абоненту ответить на него.
  2. Если вы хотите регистрировать каждое исключение, глобальный обработчик исключений подойдет. В .NET трассировка стека является объектом; его свойства могут быть проверены, как и любой другой. Вы можете записать свойства трассировки стека (даже в строковой форме) в выбранный вами журнал.
  3. Если вы хотите, чтобы каждое исключение было перехвачено, глобальный обработчик исключений должен сделать свое дело. По сути, ни одно приложение не должно быть без него.
  4. Ваши блоки улова должны только ловить исключения, которые вы знаете , из которых вы можете изящно восстановиться. То есть, если вы можете сделать что-то с этим , поймайте это. В противном случае, пусть вызывающий беспокоится об этом. Если ни один из вызывающих не может ничего с этим поделать, пусть глобальный обработчик исключений перехватит его и запишет в журнал.
...