Проверка типа внутреннего исключения
В моем коде я сталкиваюсь с ситуацией, в которой бросается System.Reflection.TargetInvocationException
. В одном конкретном случае я знаю, как я хочу обработать исключение корня, но я хочу бросить все другие исключения. Я могу придумать два способа сделать это, но я не уверен, что лучше.
1.
try
{
//code
}
catch (System.Reflection.TargetInvocationException ex)
{
if (typeof(ex.InnerException) == typeof(SpecificException))
{
//fix
}
else
{
throw ex.Innerexception;
}
}
2.
try
{
//code
}
catch (System.Reflection.TargetInvocationException ex)
{
try
{
throw ex.InnerException;
}
catch (SpecificException exSpecific)
{
//fix
}
}
Я знаю, что бросание исключений вообще происходит медленно, поэтому я чувствую, что первый метод, возможно, будет быстрее. Альтернативно, есть ли лучший способ сделать это, о котором я не думал?
Ответы
Ответ 1
У каждого из предлагаемых вами решений есть свои проблемы.
Первый метод проверяет, что тип внутреннего исключения - именно тот тип, который вы ожидаете. Это означает, что производный тип не будет соответствовать, что может быть не так, как вы планировали.
Второй метод перезаписывает внутреннюю трассировку стека исключений с текущим местоположением стека, как упоминал Дэн Пузи. Уничтожение трассировки стека может уничтожить один вывод, который вам нужен, чтобы исправить ошибку.
Решение в основном заключается в том, что DarkGray опубликовано с предложением Ника и с добавлением моего собственного (в else
):
try
{
// Do something
}
catch (TargetInvocationException ex)
{
if (ex.InnerException is SpecificException)
{
// Handle SpecificException
}
else if (ex.InnerException is SomeOtherSpecificException)
{
// Handle SomeOtherSpecificException
}
else
{
throw; // Always rethrow exceptions you don't know how to handle.
}
}
Если вы хотите повторно выбрасывать исключение, которое вы не можете обработать, не throw ex;
, так как это приведет к перезаписи трассировки стека. Вместо этого используйте throw;
, который сохраняет трассировку стека. Это в основном означает: "Я действительно не хотел вводить это предложение catch
, притворяюсь, что я никогда не поймал исключение".
Обновление: С# 6.0 предлагает гораздо лучший синтаксис с помощью фильтров исключений:
try
{
// Do something
}
catch (TargetInvocationException ex) when (ex.InnerException is SpecificException)
{
// Handle SpecificException
}
catch (TargetInvocationException ex) when (ex.InnerException is SomeOtherSpecificException)
{
// Handle SomeOtherSpecificException
}
Ответ 2
Ваш №2 определенно является интересным решением!
Вы хотите быть осторожным, хотя: TargetInvocationException
, как правило, был выброшен другим компонентом, когда он впервые поймал InnerException
. Если вы throw ex.InnerException
, вы собираетесь уничтожить некоторую информацию, содержащуюся в ней (например, трассировку стека), потому что вы повторно бросаете ее из другого места.
Итак, из двух предложенных, я бы определенно предложил пойти с №1. Я не знаю альтернативы в структуре, которую у вас есть. Тем не менее, InnerException будет выброшено изначально в другом месте - стоит исследовать, есть ли более элегантное место для обработки этого сбоя, ближе к тому месту, где выбрано исключение.
Ответ 3
try
{
//code
}
catch (System.Reflection.TargetInvocationException ex)
{
if (ex.InnerException is SpecificException)
{
//fix
}
else
{
throw ex.InnerException;
}
}
или
try
{
//code
}
catch (System.Reflection.TargetInvocationException ex)
{
SpecificException spExc = ex.InnerException as SpecificException;
if (spExc != null)
{
bla-bla spExc
}
else
{
throw ex.InnerException;
}
}
или
try
{
//code
}
catch (System.Reflection.TargetInvocationException ex)
{
if (ex.InnerException.GetType() == typeof(SpecificException))
{
//fix
}
else
{
throw ex.InnerException;
}
}