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


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

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

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

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

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

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

14 178

14 ответов:

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

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

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

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

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

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

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

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

  • необходимо немедленно показать пользователю
  • требуется дополнительная обработка, чтобы собрать вещи вместе, когда они происходят, чтобы избежать каскадных проблем (т. е.: 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);
}

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

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

// 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. всегда неизвестный исключения так высоко, как вы можете -только код, который должен использовать исключение, а не повторно бросать его, должен быть пользовательским интерфейсом или общедоступным ПРИКЛАДНОЙ ПРОГРАММНЫЙ ИНТЕРФЕЙС.

предположим, что вы подключаетесь к удаленному 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;
}

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

наконец, у нас есть пользовательский интерфейс-здесь мы не хотим иметь полностью необработанные исключения, но мы также не хотим их скрывать. Здесь мы имеем пример 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 событие.

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

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

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

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

@thewhiteambit сказал:

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

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

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

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

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

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

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

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

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

больше ответов о @ thewhiteambit concerns

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

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

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

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

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

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

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

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

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

часто кодирование по исключению может привести к дальнейшим проблемам в программном обеспечении с неправильным использованием исключений. В дополнение к использованию исключения обработка для уникальной проблемы, неправильное использование исключения принимает это далее путем выполнения кода даже после возникновения исключения. Этот плохой способ программирования напоминает метод 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. Если вы добавляете необработанное исключение, пользователь outlook может теперь знать об этом, так как outlook не уничтожит себя из-за сбоя одного плагина. И у вас есть трудное время, чтобы выяснить, что пошло не так. Поэтому второй подход в этом случай, на мой взгляд, правильный. Помимо регистрации исключения, вы можете решить отобразить сообщение об ошибке для пользователя - я рассматриваю его как бизнес-правило.

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

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

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

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

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

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

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

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

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

мой путь:

  • чтобы поймать необработанные исключения на уровне приложения (т. е. в глобальном.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
}