Проверка типа внутреннего исключения

В моем коде я сталкиваюсь с ситуацией, в которой бросается 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; 
    } 
}