Я размышляю о методе, который может вызвать исключение. Как я могу передать исключение своему вызывающему абоненту без отражения от оболочки?
Я перебрасываю InnerException, но это уничтожает трассировку стека.
Пример кода:
public void test1()
{
// Throw an exception for testing purposes
throw new ArgumentException("test1");
}
void test2()
{
try
{
MethodInfo mi = typeof(Program).GetMethod("test1");
mi.Invoke(this, null);
}
catch (TargetInvocationException tiex)
{
// Throw the new exception
throw tiex.InnerException;
}
}
Ответы:
В .NET 4.5 теперь есть
ExceptionDispatchInfo
класс.Это позволяет вам захватить исключение и повторно выдать его без изменения трассировки стека:
Это работает на любое исключение, а не только
AggregateException
.Он был введен благодаря функции
await
языка C #, которая разворачивает внутренние исключения изAggregateException
экземпляров, чтобы сделать функции асинхронного языка более похожими на функции синхронного языка.источник
throw;
после строки .Throw (), потому что компилятор не будет знать, что .Throw () всегда выдает исключение.throw;
никогда не будет вызываться в результате, но по крайней мере компилятор не будет жаловаться, если ваш метод требует возвращаемый объект или является асинхронной функцией.throw;
. Если вы используетеthrow ex.InnerException;
трассировку стека, он повторно инициализируется в точке, где он перебрасывается.ExceptionDispatchInfo.Capture(ex.InnerException ?? ex).Throw();
Это является возможным сохранение трассировки стека перед тем Повторное выбрасывание без отражения:
Это тратит много циклов по сравнению с вызовом
InternalPreserveStackTrace
через кэшированный делегат, но имеет преимущество, полагаясь только на общедоступную функциональность. Вот несколько общих шаблонов использования функций сохранения трассировки стека:источник
InternalPreserveStackTrace
(примерно на 6% медленнее при 10000 итерациях). Прямой доступ к полям с помощью отражения примерно на 2,5% быстрее, чем вызовInternalPreserveStackTrace
e.Data
словарь со строкой или уникальным ключом объекта (static readonly object myExceptionDataKey = new object ()
но не делайте этого, если вам нужно где-либо сериализовать исключения). Избегайте модификацийe.Message
, потому что где-то может быть код, который анализируетe.Message
. Разборe.Message
- это зло, но другого выбора может не быть, например, если вам нужно использовать стороннюю библиотеку с плохой практикой исключений.Я думаю, что вам лучше всего поставить это в свой блок catch:
А потом извлекать неустражение позже.
источник
Никто не объяснил разницу между
ExceptionDispatchInfo.Capture( ex ).Throw()
и равнинойthrow
, так что вот она.Полный способ отбросить перехваченное исключение - использовать
ExceptionDispatchInfo.Capture( ex ).Throw()
(доступно только в .Net 4.5).Ниже приведены случаи, необходимые для проверки этого:
1.
2.
3.
4.
Случай 1 и случай 2 дадут вам трассировку стека, где номер строки исходного кода для
CallingMethod
метода является номеромthrow new Exception( "TEST" )
строки строки.Однако случай 3 даст вам трассировку стека, где номер строки исходного кода для
CallingMethod
метода является номером строкиthrow
вызова. Это означает, что еслиthrow new Exception( "TEST" )
строка окружена другими операциями, вы не знаете, по какому номеру строки было выдано исключение.Случай 4 аналогичен случаю 2, потому что номер строки исходного исключения сохраняется, но не является реальным перебросом, потому что он меняет тип исходного исключения.
источник
Вызовите метод расширения для вашего исключения, прежде чем вы его выбросите, он сохранит исходную трассировку стека.
источник
Action<Exception>
? Здесь используется статический методЕще больше размышлений ...
Имейте в виду, что это может сломаться в любое время, так как частные поля не являются частью API. Смотрите дальнейшее обсуждение Mono Bugzilla .
источник
InternalPreserveStackTrace
метода был бы лучше, поскольку он делает то же самое и с меньшей вероятностью изменится в будущем ...Первое: не теряйте исключение TargetInvocationException - это ценная информация, когда вы захотите отладить что-либо
Во-вторых: оберните TIE как InnerException в свой собственный тип исключения и поместите свойство OriginalException, которое ссылается на то, что вам нужно (и сохраните весь стек вызовов без изменений).
Третье: пусть TIE вспыхнет из вашего метода.
источник
Ребята, вы крутые .. Я скоро стану некромантом.
источник
.Invoke()
.Другой пример кода, который использует сериализацию / десериализацию исключений. Это не требует, чтобы фактический тип исключения был сериализуемым. Также он использует только публичные / защищенные методы.
источник
Основываясь на ответе Пола Тернерса, я сделал метод расширения
return ex
IST никогда не достиг , но преимущество заключается в том, что я могу использовать вthrow ex.Capture()
качестве одного лайнера , так что компилятор не выдастnot all code paths return a value
ошибку.источник