it-swarm.com.ru

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

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

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

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

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

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

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

191
Toan Nguyen

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

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

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

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

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

Тогда я прилагаю в "попробовать/поймать"

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

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

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

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

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

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

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

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

Плохой:

// 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);
}

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

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

// 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;
}
291
Larry

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

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

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

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

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

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

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

58
Keith

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

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

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

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

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

Бросать исключения дорого.

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

  • ASP.NET: Global.asax Application_Error
  • Другие: Событие AppDomain.FirstChanceException .

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

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

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

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

@ thewhiteambit сказал:

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

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

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

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

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

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

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

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

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

Больше ответов о проблемах @thewhiteambit

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

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

  2. Синтаксический анализ также является ожидаемым случаем ( НЕ ИСКЛЮЧИТЕЛЬНЫЙ СЛУЧАЙ ). Если вы ожидаете этого, вы не используете исключения для управления потоком! . Вы получаете метаданные от пользователя, чтобы узнать, какова его/ее культура, и для этого вы используете средства форматирования! . NET также поддерживает эту и другие среды, и это исключение, поскольку следует избегать форматирования чисел, если вы ожидаете, что ваше приложение/служба будет зависеть от культуры .

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

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

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

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

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

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

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

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

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

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

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

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

32
Matías Fidemraizer

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

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

Бездумное кодирование - ЕДИНСТВЕННЫЙ вид неправильного кодирования. Тот факт, что вы чувствуете, что есть что-то лучшее, что может быть сделано в таких ситуациях, показывает, что вы вкладываете деньги в хорошее кодирование, но избегайте попыток запечатлеть какое-то общее правило в этих ситуациях и понимаете причину чего-то, что бросается в первую очередь, и что Вы можете сделать, чтобы оправиться от этого.

6
ChrisCW

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

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

5
Hamid Mosalla

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

1
fhnaseer

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

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

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

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

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
}
1
Sorcerer86pt

Второй подход хорош.

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

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

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

1
Pranay Rana

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

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

  1. Вы полагаетесь на сторонний компонент и хотите продолжить программу в случае ошибки.
  2. У вас есть экономическое обоснование, которое необходимо продолжить в случае ошибки
0
Gregory Nozik

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

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

Я могу сказать вам кое-что:

Фрагмент # 1 недопустим, потому что он игнорирует исключение. (это глотает, как будто ничего не случилось).

Так что не добавляйте блок catch, который ничего не делает или просто отбрасывает.

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

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

например, подтверждение ввода. <- Это недопустимая исключительная ситуация, скорее вы должны написать метод IsValid(myInput);, чтобы проверить, является ли элемент ввода действительным или нет.

Дизайн кода, чтобы избежать исключений. Например:

int Parse(string input);

Если мы передадим значение, которое не может быть проанализировано, в int, этот метод вызовет исключение, вместо этого мы можем написать что-то вроде этого:

bool TryParse(string input,out int result); <- этот метод возвращает логическое значение, указывающее, был ли анализ успешным.

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

0
Roxy'Pro

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

Мой путь таков:

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

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

0
Fanda

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

0
Thai Anh Duc

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

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

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

0
Jaider

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

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

0
Anirudha