Как использовать try catch для обработки исключений-лучшая практика

сохраняя код моего коллеги даже от кого-то, кто утверждает, что является старшим разработчиком, я часто вижу следующий код:

try
{
  //do something
}
catch
{
  //Do nothing
}

или иногда они пишут регистрационную информацию в Файлы журнала, такие как following try catch блок

try
{
  //do some work
}
catch(Exception exception)
{
   WriteException2LogFile(exception);
}

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

пожалуйста, дайте мне совет.

14 ответов


моя стратегия обработки исключений:

  • поймать все необработанные исключения путем подключения к Application.ThreadException event, а потом решить :

    • для приложения пользовательского интерфейса: чтобы вставить его пользователю с сообщением извинения (winforms)
    • для службы или консольного приложения: войдите в файл (службу или консоль)

тогда я всегда прилагаю каждый фрагмент кода, который выполняется внешне на try/catch :

  • все события, запущенные инфраструктурой Winforms (Load, Click, SelectedChanged...)
  • все события, запущенные сторонними компонентами

затем я заключаю в "try / catch"

  • все операции, что я знать может не работать все время (операции ввода-вывода, расчеты с нулевым делением потенциала...). В таком случае, я бросаю новый ApplicationException("custom message", innerException) в следите за тем, что действительно произошло

кроме того, я стараюсь сортировка исключения правильно. Есть исключения, которые:

  • необходимо немедленно показать пользователю
  • требуется дополнительная обработка, чтобы собрать вещи, когда они случаются, чтобы избежать каскадных проблем (т. е. put .EndUpdate в во время TreeView заполнить)
  • пользователю все равно, но важно, чтобы знаю, что случилось. Поэтому я всегда регистрирую их:

    • в журнале событий
    • или в a .файл журнала на диске

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

Я также заставляю себя попытаться:

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

Итак, наконец :

плохое:

// DON'T DO THIS, ITS BAD
try
{
    ...
}
catch 
{
   // only air...
}

бесполезно:

// DONT'T DO THIS, ITS USELESS
try
{
    ...
}
catch(Exception ex)
{
    throw ex;
}

имея попробовать, наконец, без улова совершенно справедливо:

try
{
    listView1.BeginUpdate();

    // If an exception occurs in the following code, then the finally will be executed
    // and the exception will be thrown
    ...
}
finally
{
    // I WANT THIS CODE TO RUN EVENTUALLY REGARDLESS AN EXCEPTION OCCURED OR NOT
    listView1.EndUpdate();
}

что я делаю наверху уровень:

// i.e When the user clicks on a button
try
{
    ...
}
catch(Exception ex)
{
    ex.Log(); // Log exception

    -- OR --

    ex.Log().Display(); // Log exception, then show it to the user with apologies...
}

что я делаю в некоторых вызванных функциях:

// Calculation module
try
{
    ...
}
catch(Exception ex)
{
    // Add useful information to the exception
    throw new ApplicationException("Something wrong happened in the calculation module :", ex);
}

// IO module
try
{
    ...
}
catch(Exception ex)
{
    throw new ApplicationException(string.Format("I cannot write the file {0} to {1}", fileName, directoryName), ex);
}

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

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

// Usage:

try
{
    // boom
}
catch(Exception ex)
{
    // Only log exception
    ex.Log();

    -- OR --

    // Only display exception
    ex.Display();

    -- OR --

    // Log, then display exception
    ex.Log().Display();

    -- OR --

    // Add some user-friendly message to an exception
    new ApplicationException("Unable to calculate !", ex).Log().Display();
}

// Extension methods

internal static Exception Log(this Exception ex)
{
    File.AppendAllText("CaughtExceptions" + DateTime.Now.ToString("yyyy-MM-dd") + ".log", DateTime.Now.ToString("HH:mm:ss") + ": " + ex.Message + "\n" + ex.ToString() + "\n");
    return ex;
}

internal static Exception Display(this Exception ex, string msg = null, MessageBoxImage img = MessageBoxImage.Error)
{
    MessageBox.Show(msg ?? ex.Message, "", MessageBoxButton.OK, img);
    return ex;
}

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

есть 3 обстоятельства, при которых использование try-catch имеет смысл.

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

  2. Если вам нужно что-то сделать в исключении (например, ведение журнала или откат транзакции), то повторно запустите исключение.

  3. всегда неизвестный исключения так высоко, как вы можете -только код, который должен потреблять исключение, а не повторно бросать его, должен быть UI или public ПРИКЛАДНОЙ ПРОГРАММНЫЙ ИНТЕРФЕЙС.

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

try 
{
    remoteApi.Connect()
}
catch(ApiConnectionSecurityException ex) 
{
    // User's security details have expired
    return false;
}

return true;

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

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

try
{
    DBConnection.Save();
}
catch
{
    // Roll back the DB changes so they aren't corrupted on ANY exception
    DBConnection.Rollback();

    // Re-throw the exception, it's critical that the user knows that it failed to save
    throw;
}

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

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

try
{
    // Do something
}
catch(Exception ex) 
{
    // Log exception for developers
    WriteException2LogFile(ex);

    // Display message to users
    DisplayWarningBox("An error has occurred, please contact support!");
}

однако большинство фреймворков API или UI имеют общие способы выполнения случая 3. Например ASP.Net имеет желтый экран ошибки, который сбрасывает сведения об исключении, но его можно заменить более общим сообщением в производстве окружающая среда. Следовать им лучше всего, потому что это экономит много кода, но также и потому, что ведение журнала ошибок и отображение должны быть решениями конфигурации, а не жестко закодированными.

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

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

например, предположим, что у вас большой масштаб ASP.Net применение. Регистрация ошибок может быть через ELMAH, отображение ошибок может быть информативным ysod локально и хорошим локализованным сообщением в производстве. Подключения к базе данных могут осуществляться через области транзакций и using блоки. Вам не нужен ни один try-catch блок.

TL; DR: Лучшая практика на самом деле не использовать try-catch блоки на всех.


исключение-это блокирование ошибка.

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

если ошибка блокировка, то бросать исключение. Как только исключение уже брошено, нет необходимости скрывать его, потому что оно исключительное; сообщите об этом пользователю (вы должны переформатировать все исключение на что-то полезное для пользователя в ПОЛЬЗОВАТЕЛЬСКИЙ ИНТЕРФЕЙС.)

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

например, если вы знаете, что некоторые целое вход может поставляться с недопустимым форматом, используйте int.TryParse вместо int.Parse. Есть много случаев, когда вы можете сделать это, а не просто сказать: "если это не удается, просто выбросить исключение".

исключения-это дорого.

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

  • ASP.NET:глобальные.application_error для перехвата эйсакс
  • другие: AppDomain.FirstChanceException событие.

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

для остальных случаев:

  • старайтесь избегать исключений.
  • если это не возможно: обработчики исключений первого шанса.
  • или используйте аспект PostSharp (AOP).

отвечая @thewhiteambit на некоторые комментарии...

@thewhiteambit сказал:

исключения не являются фатальными-ошибки, они являются исключениями! Иногда они это даже не ошибки, но считать их фатальными-ошибки полностью ложное понимание того, что такое исключения.

прежде всего, как исключение не может быть даже ошибка?

  • нет подключения к базе данных => исключения.
  • недопустимый формат строки для разбора на некоторый тип => exception
  • попытка проанализировать JSON и в то время как вход на самом деле не JSON => exception
  • аргумент null в то время как объект ожидался => exception
  • в некоторой библиотеке есть ошибка = > выдает неожиданное исключение
  • есть подключение сокета, и он отключается. Затем вы пытаетесь отправить сообщение => исключение
  • ...

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

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

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

кроме того, я предлагаю всем проверить fail-fast парадигма опубликовано Мартином Фаулером (и написано Джимом Шором). Вот как я всегда понимал, как с этим справляться. исключения, даже до того, как я добрался до этого документа некоторое время назад.

[...] считайте их фатальными-ошибки-это полностью ложное понимание того, что такое исключения.

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

больше ответов о @ thewhiteambit касается

например, в случае отсутствия подключения к базе данных программа может в исключительных случаях продолжайте запись в локальный файл и отправьте изменения в базе данных, как только она будет снова доступна. Ваш инвалид Литье String-to-Number можно снова попытаться проанализировать с помощью language - локальная интерпретация исключения, например, при попытке по умолчанию Английский язык Разбор ("1,5") не удается, и вы попробуете его с немецким интерпретация снова, что совершенно нормально, потому что мы используем запятую вместо точки в качестве разделителя. Вы видите, что эти исключения не должны даже блокируйте, им нужна только обработка исключений.

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

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

необработанное исключение обычно становится ошибкой, но сами исключения не являются codeproject.com/Articles/15921/Not-All-Exceptions-Are-Errors

эта статья-просто мнение или точку зрения автора.

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

[...] Использование этих исключений для обработки конкретных ошибок, возникающих продолжить программа называется coding by exception. эта анти -- картина может быстро ухудшить программное обеспечение в представлении и ремонтопригодности.

Он также говорит где-то:

неправильное использование исключений

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

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

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

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

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

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


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

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

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


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

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

  • Вы можете создать и создать новое, более конкретное исключение.

int GetInt(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch(System.IndexOutOfRangeException e)
    {
        throw new System.ArgumentOutOfRangeException(
            "Parameter index is out of range.");
    }
}
  • вы хотите частично обработать исключение, прежде чем передавать его для дополнительной обработки. В следующем примере блок catch используется для добавления записи в журнал ошибок перед повторной генерации исключения.
    try
{
    // Try to access a resource.
}
catch (System.UnauthorizedAccessException e)
{
    // Call a custom error logging procedure.
    LogError(e);
    // Re-throw the error.
    throw;     
}

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


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


второй подход является хорошим.

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

try
{
  //do some work
}
catch(Exception exception)
{
   WriteException2LogFile(exception);//it will write the or log the error in a text file
}

Я рекомендую вам перейти ко второму подходу для всего вашего приложения.


оставить пустой блок catch-это хуже всего. Если есть ошибка, лучший способ справиться с это:

  1. войдите в файл\базу данных и т. д..
  2. попробуйте исправить это на лету (возможно, пытаясь альтернативный способ сделать эту операцию)
  3. Если мы не можем исправить это, сообщите пользователю, что есть какая-то ошибка и, конечно, прервать операцию

для меня, обработка исключений может рассматриваться как бизнес-правила. Очевидно, что первый подход является неприемлемым. Второй лучше, и это может быть 100% правильный способ, если контекст так говорит. Теперь, например, вы разрабатываете Outlook Addin. Если вы добавляете необработанное исключение, пользователь outlook может теперь знать его, так как outlook не уничтожит себя из-за сбоя одного плагина. И тебе трудно понять, что пошло не так. Поэтому второй подход в этом кейс, по-моему, правильный. Помимо регистрации исключения, вы можете отобразить сообщение об ошибке пользователю - я считаю это бизнес-правилом.


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

но в реальной жизни вы можете иметь несколько ситуаций, когда вы хотите скрыть этот

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

вы должны рассмотреть эти рекомендации по дизайну для исключений

  • Исключений
  • Использование Стандартных Типов Исключений
  • исключения и производительность

https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/exceptions


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

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


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

мой путь:

  • поймать uncaughted исключений на уровне приложения (т. е. в глобальном.asax) для критических исключений (приложение не может быть полезным). Эти исключения я не ловила на месте. Просто войдите их на уровне приложения и пусть система делает свою работу.
  • поймать "на месте" и показать некоторую полезную информацию пользователю (введен неправильный номер, не может разобрать).
  • поймать на месте и ничего не делать по маргинальным проблемам, таким как "я проверю информацию об обновлении в фоновом режиме, но служба не работает".

Это определенно не должно быть лучшей практикой. ;-)


С исключениями, я пробую следующее:

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

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

в коде, что-то вроде этого:

try{
    //Some code here
}
catch(DivideByZeroException dz){
    AlerUserDivideByZerohappened();
}
catch(Exception e){
    treatGeneralException(e);
}
finally{
    //if a IO operation here i close the hanging handlers for example
}