Отлов определенных и общих исключений в c # - PullRequest
8 голосов
/ 11 сентября 2009

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

Считаете ли вы себя просто перехватом общего исключения или попыткой перехвата определенного исключения и установкой по умолчанию универсального исключения с использованием нескольких блоков перехвата?

Один из рассматриваемых кусков кода приведен ниже:

internal static bool ClearFlags(string connectionString, Guid ID)
{
    bool returnValue = false;
    SqlConnection dbEngine = new SqlConnection(connectionString);
    SqlCommand dbCmd = new SqlCommand("ClearFlags", dbEngine);
    SqlDataAdapter dataAdapter = new SqlDataAdapter(dbCmd);

    dbCmd.CommandType = CommandType.StoredProcedure;
    try
    {
        dbCmd.Parameters.AddWithValue("@ID", ID.ToString());

        dbEngine.Open();
        dbCmd.ExecuteNonQuery();
        dbEngine.Close();

        returnValue = true;
    }
    catch (Exception ex)
    { ErrorHandler(ex); }

    return returnValue;
}

Спасибо за совет

РЕДАКТИРОВАТЬ: Вот предупреждение из анализа кода

Предупреждение 351 CA1031: Microsoft.Design: Измените «ClearFlags (string, Guid)», чтобы перехватывать более конкретное исключение, чем «Exception», или повторно выбросьте исключение

Ответы [ 7 ]

25 голосов
/ 11 сентября 2009

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

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

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

Вы почти никогда не должны ловить Исключение верхнего уровня и проглатывать его. Это потому, что если вы ловите исключение верхнего уровня, вы не знаете, что именно вы обрабатываете; Абсолютно все, что могло вызвать это, так что вы почти наверняка не сможете сделать ничего, что будет правильно обрабатывать каждый случай отказа. Возможно, есть некоторые сбои, которые вы можете просто захотеть обработать и проглотить молча, но, просто проглотив исключения верхнего уровня, вы также проглотите целую кучу, которые действительно должны были быть выброшены вверх, чтобы ваш код обрабатывал выше. В вашем примере кода вы, вероятно, захотите сделать это обработать SQLException и log + проглотить это; а затем для исключения, войдите и сбросьте его. Это охватывает себя. Вы все еще регистрируете все типы исключений, но вы глотаете только достаточно предсказуемое SQLException, которое указывает на проблемы с вашим SQL / базой данных.

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

7 голосов
/ 11 сентября 2009

Взгляните на эту статью Кшиштофа Квалины, которая мне показалась очень полезной для понимания, когда ловить или игнорировать исключения:

Как проектировать иерархии исключений

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

  • Ошибки использования , например DivideByZeroException, которые указывают на ошибки в коде; Вы не должны обрабатывать их, потому что их можно избежать, изменив код.
  • Логические ошибки , такие как FileNotFoundException, с которыми вам нужно справиться, потому что вы не можете гарантировать, что они не произойдут. (Даже если вы проверите наличие файла, он все равно может быть удален за долю секунды до того, как вы прочитаете его.)
  • Системные сбои , такие как OutOfMemoryException, которые вы не можете избежать или обработать.
2 голосов
/ 11 сентября 2009

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

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

1 голос
/ 04 апреля 2010

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

  1. Как упомянуто выше, если я собираю какую-то полезную информацию для регистрации, а затем перебрасываю.

  2. Если я выполняю асинхронную операцию, такую ​​как обработка сообщений в очереди или заданий в рабочем потоке, и я хочу перехватить исключение для повторного выброса в другом контексте. Я также часто использую некрасивый хак, который обманывает CLR, добавляя информацию трассировки стека, чтобы он не терялся при повторном отбрасывании в новом контексте.

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

1 голос
/ 11 сентября 2009

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

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

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

1 голос
/ 11 сентября 2009

Да,

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

Например, если вы делали веб-запрос, сначала вы должны отследить такие вещи, как TimeOuts и 404, а затем сообщить конечному пользователю, что он должен повторить попытку (тайм-аут), и / или проверить введенный им URL.

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

0 голосов
/ 11 сентября 2009

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

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

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

...