Почему поймать и повторное выбрасывание исключения в C #?

Обновить

November 2018

Просмотры

180.6k раз

470

Я смотрю на статью C # - Передача данных объекта на сериализуемых DTOS.

В статье этот кусок кода:

public static string SerializeDTO(DTO dto) {
    try {
        XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
        StringWriter sWriter = new StringWriter();
        xmlSer.Serialize(sWriter, dto);
        return sWriter.ToString();
    }
    catch(Exception ex) {
        throw ex;
    }
}

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

Ergo:

public static string SerializeDTO(DTO dto) {
    XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
    StringWriter sWriter = new StringWriter();
    xmlSer.Serialize(sWriter, dto);
    return sWriter.ToString();
}

Или я что-то отсутствует основополагающий об обработке ошибок в C #? Это в значительной степени так же, как Java (минус отмеченные исключения), не так ли? ... То есть, они оба утонченный C ++.

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


РЕДАКТИРОВАТЬ:

Просто резюмировать для тех, кто считает эту нить в будущем ...

НЕ ДЕЛАЙТЕ

try {
    // Do stuff that might throw an exception
}
catch (Exception e) {
    throw e; // This destroys the strack trace information!
}

Информация трассировки стека может иметь решающее значение для определения причины проблемы!

ДЕЛАТЬ

try {
    // Do stuff that might throw an exception
}
catch (SqlException e) {
    // Log it
    if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
        // Do special cleanup, like maybe closing the "dirty" database connection.
        throw; // This preserves the stack trace
    }
}
catch (IOException e) {
    // Log it
    throw;
}
catch (Exception e) {
    // Log it
    throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
    // Normal clean goes here (like closing open files).
}

Поймать более конкретные исключения до менее специфические (так же, как Java).


Рекомендации:

32 ответы

12

Моя главная причина для такой код:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

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

Они хороши во время отладки, хотя.

12

Моя главная причина для такой код:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

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

Они хороши во время отладки, хотя.

2

К сожалению, но много примеров, как «улучшенный дизайн» до сих пор пахнут ужасно или может быть очень обманчивым. Имея TRY {} {поймать журнал; бросок} это просто совершенно бессмысленно. протоколирование Исключение должно быть сделано в центральном месте внутри приложения. исключения пузыря вверх StackTrace в любом случае, почему бы не зарегистрировать их где-то и близко к границам системы?

С осторожностью следует применять, когда вы сериализовать контекст (т.е. DTO в одном данном примере) только в лог сообщения. Он может легко содержать конфиденциальную информацию, можно не хочет дойдут руки всех людей, которые могут получить доступ к лог-файлам. И если вы не добавить любую новую информацию, за исключением, я действительно не вижу смысла исключения обертке. Хороший старый Java имеет какой-то момент для этого, она требует абонента знать, какие исключения следует ожидать последующего вызова кода. Так как вы не имеете это в .NET, упаковка не делает ничего хорошего, по крайней мере, 80% случаев я видел.

101

Не делайте этого,

try 
{
...
}
catch(Exception ex)
{
   throw ex;
}

Вы потеряете информацию трассировки стека ...

Либо делать,

try { ... }
catch { throw; }

ИЛИ ЖЕ

try { ... }
catch (Exception ex)
{
    throw new Exception("My Custom Error Message", ex);
}

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

try
{
   ...
}
catch(SQLException sex)
{
   //Do Custom Logging 
   //Don't throw exception - swallow it here
}
catch(OtherException oex)
{
   //Do something else
   throw new WrappedException("Other Exception occured");
}
catch
{
   System.Diagnostics.Debug.WriteLine("Eeep! an error, not to worry, will be handled higher up the call stack");
   throw; //Chuck everything else back up the stack
}
2

Извините, но много примеров, как «улучшенный дизайн» до сих пор пахнут ужасно или может быть крайне обманчивым. Имея TRY {} {поймать журнал; бросок} это просто совершенно бессмысленно. протоколирование Исключение должно быть сделано в центральном месте внутри приложения. исключения пузыря вверх StackTrace в любом случае, почему бы не зарегистрировать их где-то и близко к границам системы?

С осторожностью следует применять, когда вы сериализовать контекст (т.е. DTO в одном данном примере) только в лог сообщения. Он может легко содержать конфиденциальную информацию, можно не хочет дойдут руки всех людей, которые могут получить доступ к лог-файлам. И если вы не добавить любую новую информацию, за исключением, я действительно не вижу смысла исключения обертке. Хороший старый Java имеет какой-то момент для этого, она требует абонента знать, какие исключения следует ожидать последующего вызова кода. Так как вы не имеете это в .NET, упаковка не делает ничего хорошего, по крайней мере, 80% случаев я видел.

5

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

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

В большинстве языков .NET, единственный способ для кода , чтобы принять меры , основываясь на исключение является catchего (даже если он знает , что не собирается разрешить исключение), выполнить действие , о котором идет речь , а затем повторно throw). Другой возможный подход , если код не заботится о том , что выброшенное исключение является использование okфлага с try/finallyблоком; установить okфлаг falseперед блоком и trueперед блоком выходов, и до того , returnчто это в пределах блока. Затем, в течение finally, предположим , что , если okне установлено, исключение должно иметь место. Такой подход является семантический лучше , чем catch/ throw, но это некрасиво , и меньше , чем в обслуживании она должна быть.

369

Первый; так что код в статье делает это зло. throw exсбросит стек вызовов в исключении до точки , где это бросок утверждение является; потери информации о том, где исключением фактически была создана.

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

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

try 
{
    // code that may throw exceptions    
}
catch(Exception ex) 
{
    // add error logging here
    throw;
}
50

C # (до C # 6) не поддерживает КСС «отфильтрованы исключения», который делает VB, так и в C # 1-5 одна причина для повторного выбрасывания исключения является то, что у вас нет достаточно информации, в то время улова () чтобы определить, хотите ли вы на самом деле поймать исключение.

Например, в VB вы можете сделать

Try
 ..
Catch Ex As MyException When Ex.ErrorCode = 123
 .. 
End Try

... который не будет обрабатывать MyExceptions с различными значениями ErrorCode. В C # до v6, вы должны поймать и вновь бросить MyException если ErrorCode не было 123:

try 
{
   ...
}
catch(MyException ex)
{
    if (ex.ErrorCode != 123) throw;
    ...
}

Поскольку C # 6.0 вы можете выбирать так же , как с VB:

try 
{
  // Do stuff
} 
catch (Exception e) when (e.ErrorCode == 123456) // filter
{
  // Handle, other exceptions will be left alone and bubble up
}
2

К сожалению, но много примеров, как «улучшенный дизайн» до сих пор пахнут ужасно или может быть очень обманчивым. Имея TRY {} {поймать журнал; бросок} это просто совершенно бессмысленно. протоколирование Исключение должно быть сделано в центральном месте внутри приложения. исключения пузыря вверх StackTrace в любом случае, почему бы не зарегистрировать их где-то и близко к границам системы?

С осторожностью следует применять, когда вы сериализовать контекст (т.е. DTO в одном данном примере) только в лог сообщения. Он может легко содержать конфиденциальную информацию, можно не хочет дойдут руки всех людей, которые могут получить доступ к лог-файлам. И если вы не добавить любую новую информацию, за исключением, я действительно не вижу смысла исключения обертке. Хороший старый Java имеет какой-то момент для этого, она требует абонента знать, какие исключения следует ожидать последующего вызова кода. Так как вы не имеете это в .NET, упаковка не делает ничего хорошего, по крайней мере, 80% случаев я видел.

3

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

Можно сказать , Catch io.FileNotFoundExeption exи затем использовать альтернативный путь к файлу или некоторые такие, но все - таки бросить ошибку.

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

3

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

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

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

try
{
    Cursor.Current = Cursors.WaitCursor;
    // Test something
    if (testResult) return;
    // Do something else
}
catch
{
    throw;
}
finally
{
     Cursor.Current = Cursors.Default;
}
13

Моя главная причина для такой код:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

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

Они хороши во время отладки, хотя.

3

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

7

Вы не хотите , чтобы бросить экс - как это потеряет стек вызовов. См Exception Handling (MSDN).

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

2

К сожалению, но много примеров, как «улучшенный дизайн» до сих пор пахнут ужасно или может быть очень обманчивым. Имея TRY {} {поймать журнал; бросок} это просто совершенно бессмысленно. протоколирование Исключение должно быть сделано в центральном месте внутри приложения. исключения пузыря вверх StackTrace в любом случае, почему бы не зарегистрировать их где-то и близко к границам системы?

С осторожностью следует применять, когда вы сериализовать контекст (т.е. DTO в одном данном примере) только в лог сообщения. Он может легко содержать конфиденциальную информацию, можно не хочет дойдут руки всех людей, которые могут получить доступ к лог-файлам. И если вы не добавить любую новую информацию, за исключением, я действительно не вижу смысла исключения обертке. Хороший старый Java имеет какой-то момент для этого, она требует абонента знать, какие исключения следует ожидать последующего вызова кода. Так как вы не имеете это в .NET, упаковка не делает ничего хорошего, по крайней мере, 80% случаев я видел.

2

К сожалению, но много примеров, как «улучшенный дизайн» до сих пор пахнут ужасно или может быть очень обманчивым. Имея TRY {} {поймать журнал; бросок} это просто совершенно бессмысленно. протоколирование Исключение должно быть сделано в центральном месте внутри приложения. исключения пузыря вверх StackTrace в любом случае, почему бы не зарегистрировать их где-то и близко к границам системы?

С осторожностью следует применять, когда вы сериализовать контекст (т.е. DTO в одном данном примере) только в лог сообщения. Он может легко содержать конфиденциальную информацию, можно не хочет дойдут руки всех людей, которые могут получить доступ к лог-файлам. И если вы не добавить любую новую информацию, за исключением, я действительно не вижу смысла исключения обертке. Хороший старый Java имеет какой-то момент для этого, она требует абонента знать, какие исключения следует ожидать последующего вызова кода. Так как вы не имеете это в .NET, упаковка не делает ничего хорошего, по крайней мере, 80% случаев я видел.

2

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

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

Для того, чтобы повторно итерация хотя Там нет смысла поймать исключение в данном примере вы публикуемые. НЕ делать это так!

1

Большинство ответов говорят о сценарии броской лог-Rethrow.

Вместо того , чтобы писать в коде рассмотреть возможность использования АОП, в частности Postsharp.Diagnostic.Toolkit с OnExceptionOptions IncludeParameterValue и IncludeThisArgument

3

Это может быть полезно, когда ваши функции программирования для библиотеки или библиотек DLL.

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

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

12

Моя главная причина для такой код:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

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

Они хороши во время отладки, хотя.

12

Моя главная причина для такой код:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

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

Они хороши во время отладки, хотя.

12

Моя главная причина для такой код:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

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

Они хороши во время отладки, хотя.

1

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

2

К сожалению, но много примеров, как «улучшенный дизайн» до сих пор пахнут ужасно или может быть очень обманчивым. Имея TRY {} {поймать журнал; бросок} это просто совершенно бессмысленно. протоколирование Исключение должно быть сделано в центральном месте внутри приложения. исключения пузыря вверх StackTrace в любом случае, почему бы не зарегистрировать их где-то и близко к границам системы?

С осторожностью следует применять, когда вы сериализовать контекст (т.е. DTO в одном данном примере) только в лог сообщения. Он может легко содержать конфиденциальную информацию, можно не хочет дойдут руки всех людей, которые могут получить доступ к лог-файлам. И если вы не добавить любую новую информацию, за исключением, я действительно не вижу смысла исключения обертке. Хороший старый Java имеет какой-то момент для этого, она требует абонента знать, какие исключения следует ожидать последующего вызова кода. Так как вы не имеете это в .NET, упаковка не делает ничего хорошего, по крайней мере, 80% случаев я видел.

2

К сожалению, но много примеров, как «улучшенный дизайн» до сих пор пахнут ужасно или может быть очень обманчивым. Имея TRY {} {поймать журнал; бросок} это просто совершенно бессмысленно. протоколирование Исключение должно быть сделано в центральном месте внутри приложения. исключения пузыря вверх StackTrace в любом случае, почему бы не зарегистрировать их где-то и близко к границам системы?

С осторожностью следует применять, когда вы сериализовать контекст (т.е. DTO в одном данном примере) только в лог сообщения. Он может легко содержать конфиденциальную информацию, можно не хочет дойдут руки всех людей, которые могут получить доступ к лог-файлам. И если вы не добавить любую новую информацию, за исключением, я действительно не вижу смысла исключения обертке. Хороший старый Java имеет какой-то момент для этого, она требует абонента знать, какие исключения следует ожидать последующего вызова кода. Так как вы не имеете это в .NET, упаковка не делает ничего хорошего, по крайней мере, 80% случаев я видел.

12

Моя главная причина для такой код:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

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

Они хороши во время отладки, хотя.

2

К сожалению, но много примеров, как «улучшенный дизайн» до сих пор пахнут ужасно или может быть очень обманчивым. Имея TRY {} {поймать журнал; бросок} это просто совершенно бессмысленно. протоколирование Исключение должно быть сделано в центральном месте внутри приложения. исключения пузыря вверх StackTrace в любом случае, почему бы не зарегистрировать их где-то и близко к границам системы?

С осторожностью следует применять, когда вы сериализовать контекст (т.е. DTO в одном данном примере) только в лог сообщения. Он может легко содержать конфиденциальную информацию, можно не хочет дойдут руки всех людей, которые могут получить доступ к лог-файлам. И если вы не добавить любую новую информацию, за исключением, я действительно не вижу смысла исключения обертке. Хороший старый Java имеет какой-то момент для этого, она требует абонента знать, какие исключения следует ожидать последующего вызова кода. Так как вы не имеете это в .NET, упаковка не делает ничего хорошего, по крайней мере, 80% случаев я видел.

12

Моя главная причина для такой код:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

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

Они хороши во время отладки, хотя.

12

Моя главная причина для такой код:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

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

Они хороши во время отладки, хотя.

10

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

public static string SerializeDTO(DTO dto) {
  try {
      XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
      StringWriter sWriter = new StringWriter();
      xmlSer.Serialize(sWriter, dto);
      return sWriter.ToString();
  }
  catch(Exception ex) {
    string message = 
      String.Format("Something went wrong serializing DTO {0}", DTO);
    throw new MyLibraryException(message, ex);
  }
}
2

К сожалению, но много примеров, как «улучшенный дизайн» до сих пор пахнут ужасно или может быть очень обманчивым. Имея TRY {} {поймать журнал; бросок} это просто совершенно бессмысленно. протоколирование Исключение должно быть сделано в центральном месте внутри приложения. исключения пузыря вверх StackTrace в любом случае, почему бы не зарегистрировать их где-то и близко к границам системы?

С осторожностью следует применять, когда вы сериализовать контекст (т.е. DTO в одном данном примере) только в лог сообщения. Он может легко содержать конфиденциальную информацию, можно не хочет дойдут руки всех людей, которые могут получить доступ к лог-файлам. И если вы не добавить любую новую информацию, за исключением, я действительно не вижу смысла исключения обертке. Хороший старый Java имеет какой-то момент для этого, она требует абонента знать, какие исключения следует ожидать последующего вызова кода. Так как вы не имеете это в .NET, упаковка не делает ничего хорошего, по крайней мере, 80% случаев я видел.

10

Разве это точно эквивалентно не обработки исключений на всех?

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

Связанные вопросы