разница между броском и броском нового исключения ()


164

в чем разница между

try { ... }
catch{ throw } 

и

try{ ... }
catch(Exception e) {throw new Exception(e.message) } 

Независимо от того, что второй показывает сообщение?


51
Второй фрагмент - одна из самых злых (но безобидных) строк кода, которые я когда-либо видел.
— SLaks

Ответы:


259

throw; сбрасывает исходное исключение и сохраняет исходный след стека.

throw ex;генерирует исходное исключение, но сбрасывает трассировку стека, уничтожая всю информацию трассировки стека до вашего catchблока.


НИКОГДА не пишитеthrow ex;


throw new Exception(ex.Message);еще хуже. Он создает новый Exceptionэкземпляр, теряя исходную трассировку стека исключения, а также его тип. (например, IOException).
Кроме того, некоторые исключения содержат дополнительную информацию (например, ArgumentException.ParamName).
throw new Exception(ex.Message); уничтожит эту информацию тоже.

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

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


24
«бросить новое исключение (бывшее); еще хуже». Я не согласен с этим. Иногда вы хотите изменить тип исключения, и тогда лучше всего сохранить исходное исключение как внутреннее исключение. Хотя это должно быть throw new MyCustomException(myMessage, ex);конечно.
— Дирк Воллмар

9
@ 0xA3: Я имел в виду ex.Message, что это хуже.
— SLaks

6
В дополнение к реализации стандартных конструкторов, следует также сделать пользовательские исключения [Serializable()].
— Дирк Воллмар

21
Да, черт, мы любим вас как исключения, поэтому мы помещаем исключение в ваше исключение, чтобы вы могли ловить, пока вы ловите.
— Дарт Континент,

2
@SLaks: Когда вы указали throw;фактический номер строки, где произошло исключение, номером строки throw;. Как вы предлагаете справиться с этим? stackoverflow.com/questions/2493779/…
— Эрик Дж.

34

Первый сохраняет оригинальную трассировку стека:

try { ... }
catch
{
    // Do something.
    throw;
}

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

try { ... } catch (Exception e)
{
    throw new BarException("Something broke!");
}

Есть также третий способ, где вы передаете внутреннее исключение:

try { ... }
catch (FooException e) {
    throw new BarException("foo", e);
} 

Я бы порекомендовал использовать:

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

6

Еще один момент, который я не видел, чтобы кто-то высказывал:

Если вы ничего не делаете в своем блоке catch {}, делать попытку ... catch бессмысленно. Я вижу это все время:

try 
{
  //Code here
}
catch
{
    throw;
}

Или хуже:

try 
{
  //Code here
}
catch(Exception ex)
{
    throw ex;
}

Хуже всего:

try 
{
  //Code here
}
catch(Exception ex)
{
    throw new System.Exception(ex.Message);
}

Я согласен, если у вас нет окончательного положения.
— Тони Россманн

1
@ToniRossmann В этом случае я бы использовал try..finally без подвоха, если только вы не делаете что-то кроме throw;
— JLWarlow

4

throwповторно генерирует перехваченное исключение, сохраняя трассировку стека, при этом throw new Exceptionтеряя некоторые детали перехваченного исключения.

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

У BlackWasp есть хорошая статья под названием « Исключения в C #» .


4

Создание нового исключения сносит текущую трассировку стека.

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

catch(Exception e)
{
    throw new CustomException(customMessage, e);
}

3

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

Использование throwбез каких-либо аргументов сохраняет стек вызовов для целей отладки.


0

Если вы хотите, вы можете выдать новое Исключение, с исходным, установленным как внутреннее исключение.


0

Ваш второй пример сбросит трассировку стека исключения. Первый наиболее точно сохраняет происхождение исключения. Кроме того, вы развернули исходный тип, который является ключевым в знании того, что на самом деле пошло не так ... Если для функциональности требуется второе - например, для добавления расширенной информации или повторной упаковки со специальным типом, таким как пользовательское «HandleableException», тогда просто Убедитесь, что свойство InnerException также установлено!


Да, это один из тех вопросов, где вы должны писать быстро. ;)
— Роберт Харви

0

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

public void MyMethod ()
{
    // both can throw IOException
    try { foo(); } catch { throw; }
    try { bar(); } catch(E) {throw new Exception(E.message); }
}

(...)

try {
    MyMethod ();
} catch (IOException ex) {
    Console.WriteLine ("Error with I/O"); // [1]
} catch (Exception ex) {
    Console.WriteLine ("Other error");    // [2]
}

Если foo()бросает IOException, [1]блок catch поймает исключение. Но когда bar()броски IOException, он будет преобразован в простого Exceptionмуравья, который не будет пойман [1]блоком catch.


0

throw или throw ex, оба используются для выброса или повторного выброса исключения, когда вы просто регистрируете информацию об ошибке и не хотите отправлять какую-либо информацию вызывающей стороне, вы просто регистрируете ошибку в catch и exit. Но если вы хотите отправить значимую информацию об исключении вызывающей стороне, которую вы используете throw или throw ex. Теперь разница между throw и throw ex заключается в том, что throw сохраняет трассировку стека и другую информацию, но throw ex создает новый объект исключения, и, следовательно, исходная трассировка стека теряется. Поэтому, когда мы должны использовать throw и throw e, есть еще несколько ситуаций, в которых вы можете захотеть перебросить исключение, такое как сброс информации о стеке вызовов. Например, если метод находится в библиотеке, и вы хотите скрыть детали библиотеки из вызывающего кода, Вы не обязательно хотите, чтобы стек вызовов включал информацию о частных методах в библиотеке. В этом случае вы можете перехватывать исключения в открытых методах библиотеки, а затем перебрасывать их так, чтобы стек вызовов начинался с этих открытых методов.


0

Бросьте; Восстановите исходное исключение и сохраните тип исключения.

Бросить новое исключение (); Восстановить исходный тип исключения и сбросить трассировку стека исключений

Брось экс; Сброс трассировки стека исключений и сброс типа исключения


-1

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

using System;
using System.Collections.Generic;

namespace ExceptionDemo
{
   class Program
   {
      static void Main(string[] args)
      {
         void fail()
         {
            (null as string).Trim();
         }

         void bareThrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw;
            }
         }

         void rethrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw e;
            }
         }

         void innerThrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw new Exception("outer", e);
            }
         }

         var cases = new Dictionary<string, Action>()
         {
            { "Bare Throw:", bareThrow },
            { "Rethrow", rethrow },
            { "Inner Throw", innerThrow }
         };

         foreach (var c in cases)
         {
            Console.WriteLine(c.Key);
            Console.WriteLine(new string('-', 40));
            try
            {
               c.Value();
            } catch (Exception e)
            {
               Console.WriteLine(e.ToString());
            }
         }
      }
   }
}

Который генерирует следующий вывод:

Bare Throw:
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
   at ExceptionDemo.Program.<>c.<Main>g__bareThrow|0_1() in C:\...\ExceptionDemo\Program.cs:line 19
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

Rethrow
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<>c.<Main>g__rethrow|0_2() in C:\...\ExceptionDemo\Program.cs:line 35
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

Inner Throw
----------------------------------------
System.Exception: outer ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
   at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 43
   --- End of inner exception stack trace ---
   at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 47
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

Голый бросок, как указано в предыдущих ответах, ясно показывает как исходную строку кода, который не прошел (строка 12), так и две другие точки, активные в стеке вызовов, когда произошло исключение (строки 19 и 64).

Вывод случая повторного выброса показывает, почему это проблема. Когда исключение перебрасывается подобным образом, исключение не будет включать информацию об исходном стеке. Обратите внимание, что throw eвключены только (строка 35) и самая внешняя точка стека вызовов (строка 64). Было бы трудно отследить метод fail () как источник проблемы, если вы выбрасываете исключения таким образом.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.