Подтвердить что ты не робот

Есть ли какая-нибудь реальная причина использовать throw ex?

В С#, throw ex почти всегда ошибочно, так как он сбрасывает трассировку стека.

Мне просто интересно, есть ли для этого какое-то реальное использование? Единственная причина, по которой я могу думать, это скрыть внутренности вашей закрытой библиотеки, но это действительно слабая причина. Кроме того, я никогда не встречался в реальном мире.

Изменить: Я имею в виду throw ex, как в том, что бросает то же самое исключение, которое было поймано, но с пустым стеклом, как при неправильном выполнении. Я знаю, что throw ex должен существовать как конструктор языка, чтобы позволить бросать другое исключение (throw new DifferentException("ex as innerException", ex)), и было просто интересно, есть ли когда-либо синтаксис, где throw ex не ошибается.

4b9b3361

Ответ 1

Нет причин для повторного запуска объектов исключения, то есть "throw ex" .

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

    //Possible but not useful.
    object ref1 = null;

    ref1.ToString();

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

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

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

Ответ 2

Я никогда не видел, чтобы это использовалось специально, но это, конечно, ничего не значит.

Посмотрите на альтернативы:

catch(Exception ex)
{
    // do stuff (logging)
    throw;                                // a) continue ex
    throw new SomeException("blah", ex);  // b) wrap
    throw new SomeException("blah");      // c) replace
    throw ex;                             // d) reset stack-trace
}

Оба c) и d) отбрасывают трассировку стека, единственное "преимущество" d), которое я вижу, это то, что throw ex; сохраняет точный тип и возможные дополнительные свойства (ошибка SQL) ex.

Итак, существует ли когда-либо случай, когда вы хотите хранить всю информацию об исключении, кроме трассировки стека? Не нормально, но некоторые предположения:

  • на границе обфусканной библиотеки. Но c) будет выглядеть как лучший вариант здесь.
  • на сайте вызова на некоторый сгенерированный код, например, в классе RegEx.

Ответ 3

throw ex - это просто ошибка или опечатка, сделанная людьми, которые не знают и не забывают о throw;.

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

Правило FxCop CA2200 RethrowToPreserveStackDetails идет таким же образом, приглашая вас в реконструировать исключение, используя throw;:

try
{
    // ...
}
catch (SomeException ex)
{
    Logger.AddException(ex);
    throw;
}

Ответ 4

Если вы перестали думать об этом, throw ex фактически используется довольно часто - за исключением неназванной локальной переменной. Было бы сложно (переконструировать) язык так, чтобы:

throw new ArgumentException();

но

var ex = new ArgumentException();
throw ex;

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

Ответ 5

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

Отражение

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

Если вы используете .NET 4.5+, вы можете использовать ExceptionDispatchInfo, чтобы сохранить исходную трассировку стека:

MethodInfo someMethod = ...
try
{
    someMethod.Invoke();
}
catch (TargetInvocationException e)
{
    ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}

API задач

В .NET Task API исключения, выполняемые во время выполнения задач, завернуты в AggregateException. При использовании async/wait инфраструктура будет выполнять разворачивание под обложками, чтобы генерировать исходное исключение вместо оболочки AggregateException.

Помимо кода BCL, требующего повторного удаления существующих исключений при автоматической разворачиваемости, бывают случаи, когда вы не можете использовать async/wait и, следовательно, необходимо вручную разворачивать и реконструировать. Для практического примера этого, проверьте внутренние функции TaskExtensions.WaitAndUnwrapException в AsyncContext в Stephen Clearys отличный AsyncEx.

Ответ 6

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

Допустим, у вас есть:

Public int GetAllCustomers()
{
     try
     {
          return data.GetAllCustomers()
     }
     catch()
     {
         Logger.log("Error calling GetAllCustomers");
     }
}
     //data
Public static int GetAllCustomers()
{
     try
     {
          //db operations...
     }
     catch(Exception ex)
     {
          Logger.log(ex.Stacktrace);
          throw ex;
     }
}