2012-04-12 12 views
11

Nel mio codice mi sto imbattendo in una situazione in cui viene generato un System.Reflection.TargetInvocationException. In un caso specifico so come voglio gestire l'eccezione di root, ma voglio lanciare tutte le altre eccezioni. Posso pensare a due modi per farlo, ma non sono sicuro di quale sia il migliore.Controllo del tipo di un'eccezione interna

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 
    } 
} 

Sono consapevole che generare eccezioni, in generale, è lento, quindi mi sento il primo metodo sarebbe forse essere più veloce. In alternativa, c'è un modo migliore per farlo a cui non ho pensato?

+2

2 è affascinante, imho 1 è più leggibile e probabilmente meglio da un punto di vista delle prestazioni. – Gabber

+0

Domanda: qual è la chiamata che lancia "TargetInvocationException"? È il tuo codice o di terze parti? –

+0

E 'generato il codice che sta leggendo dal db. – geekchic

risposta

17

Ciascuna delle soluzioni proposte ha il suo problema.

Il primo metodo verifica che il tipo di eccezione interna sia esattamente il tipo che ci si aspetta. Ciò significa che un tipo derivato non corrisponderà, il che potrebbe non essere quello che intendevi.

Il secondo metodo sovrascrive la traccia dello stack dell'eccezione interna con la posizione dello stack corrente, come menzionato da Dan Puzey. Distruggere la traccia dello stack potrebbe distruggere l'unico lead necessario per correggere un bug.

La soluzione è fondamentalmente ciò che darkgray pubblicato, con il suggerimento di Nick e con una suggestione aggiunto di mio (nel 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. 
    } 
} 

Se si vuole ri-generare un'eccezione che risulta si può 't man, do not throw ex; poiché sovrascriverà la traccia dello stack. Utilizzare invece throw; che conserva la traccia dello stack. Significa fondamentalmente "In realtà non volevo inserire questa clausola catch, fingere di non aver mai rilevato l'eccezione".

Aggiornamento: C# 6.0 offre una sintassi molto migliore via Filtri eccezione:

try 
{ 
    // Do something 
} 
catch (TargetInvocationException ex) when (ex.InnerException is SpecificException) 
{ 
    // Handle SpecificException 
} 
catch (TargetInvocationException ex) when (ex.InnerException is SomeOtherSpecificException) 
{ 
    // Handle SomeOtherSpecificException 
} 
+0

+1 per indicare la differenza tra 'throw;' e 'throw ex;' – geekchic

-2
try 
{ 
    //code 
} 
catch (System.Reflection.TargetInvocationException ex) 
{ 
    if (ex.InnerException is SpecificException) 
    { 
     //fix 
    } 
    else 
    { 
     throw ex.InnerException; 
    } 
} 

o

try 
{ 
    //code 
} 
catch (System.Reflection.TargetInvocationException ex) 
{ 
    SpecificException spExc = ex.InnerException as SpecificException; 
    if (spExc != null) 
    { 
     bla-bla spExc 
    } 
    else 
    { 
     throw ex.InnerException; 
    } 
} 

o

try 
{ 
    //code 
} 
catch (System.Reflection.TargetInvocationException ex) 
{ 
    if (ex.InnerException.GetType() == typeof(SpecificException)) 
    { 
     //fix 
    } 
    else 
    { 
     throw ex.InnerException; 
    } 
} 
+0

Perché non usare la parola chiave 'is'? – Nick

+0

Il tuo codice non è diverso dal punto di vista funzionale del primo suggerimento e non hai fornito alcun ragionamento o giustificazione per il codice. –

1

tuo # 2 è sicuramente una soluzione interessante!

Non vuole stare attenti però: TargetInvocationException in genere sono stati gettati da un altro componente, quando prima catturatoInnerException. Se hai throw ex.InnerException stai per distruggere alcune informazioni che contiene (come la traccia dello stack) perché stai rilanciandolo da una posizione diversa.

Quindi, tra i due che hai proposto, ti suggerirei di andare con il numero 1. Non sono a conoscenza di un'alternativa all'interno della struttura che hai. Tuttavia, InnerException sarà stato originariamente lanciato altrove - vale la pena di indagare se c'è un posto più elegante per gestire questo fallimento, più vicino a dove viene lanciata l'eccezione.