2010-10-06 11 views
66

Quando C# genera un'eccezione, può presentare un'eccezione interna. Quello che voglio fare è ottenere l'eccezione più interna, o in altre parole, l'eccezione foglia che non ha un'eccezione interna. Posso fare questo in un ciclo while:Trova l'eccezione più interna senza utilizzare un ciclo while?

while (e.InnerException != null) 
{ 
    e = e.InnerException; 
} 

ma mi chiedevo se ci fosse qualche one-liner potrei usare per fare questo, invece.

+4

sto buttando un'eccezione in una delle mie classi, ma la mia classe è utilizzato da una libreria che inghiotte tutte le eccezioni e lancia la propria. Il problema è che l'eccezione della libreria è molto generica e ho bisogno di sapere quale specifica eccezione ho lanciato per sapere come gestire il problema. A peggiorare le cose, la libreria genererà la propria eccezione più volte annidata l'una nell'altra, a una profondità arbitraria. Ad esempio, pubblicherà 'LibraryException -> LibraryException -> LibraryException -> MyException'. La mia eccezione è sempre l'ultima della catena e non ha la sua eccezione interna. –

+0

Oh, capisco perché potresti scendere al più profondo, avendolo fatto io stesso (e le varianti, come quelle più interne che derivano da un particolare tipo), ma non capisco quale sia il problema con ciò che hai già. –

+0

Oh, capisco cosa intendi. Mi stavo chiedendo se ci fosse un altro modo di scriverlo. Mi sono abituato a usare 'LINQ' per fare quasi tutto ciò che mi sembra strano quando devo scrivere un ciclo. Lavoro principalmente con l'accesso ai dati, quindi lavoro praticamente con 'ICollections' per quasi tutto ciò che faccio. –

risposta

102

Oneliner :)

while (e.InnerException != null) e = e.InnerException; 

Ovviamente, non si può far più semplice.

Come detto in this answer da Glenn McElhoe, è l'unico modo affidabile.

+6

metodo GetBaseException() fa questo w/oa loop. http://msdn.microsoft.com/en-us/library/system.exception.getbaseexception.aspx – codingoutloud

+7

Questo funziona considerando che 'Exception.GetBaseException()' non ha funzionato per me. – Tarik

1

È possibile utilizzare la ricorsione per creare un metodo in una classe di utilità da qualche parte.

public Exception GetFirstException(Exception ex) 
{ 
    if(ex.InnerException == null) { return ex; } // end case 
    else { return GetFirstException(ex.InnerException); } // recurse 
} 

Usa:

try 
{ 
    // some code here 
} 
catch (Exception ex) 
{ 
    Exception baseException = GetFirstException(ex); 
} 

Il metodo di estensione suggerito (buona idea @dtb)

public static Exception GetFirstException(this Exception ex) 
{ 
    if(ex.InnerExecption == null) { return ex; } // end case 
    else { return GetFirstException(ex.InnerException); } // recurse 
} 

Usa:

try 
{ 
    // some code here 
} 
catch (Exception ex) 
{ 
    Exception baseException = ex.GetFirstException(); 
} 
+0

** Utile ** 'public static Exception GetOriginalException (questa eccezione ex) { if (ex.InnerException == null) return ex; return ex.InnerException.GetOriginalException(); } ' – Kiquenet

+0

C'è un errore di battitura, dovrebbe essere InnerException not InnerExection –

11

Se non sai quanto è profonda le eccezioni interne sono annidate, non c'è modo di aggirare un ciclo o ricorsione.

Naturalmente, è possibile definire un metodo di estensione che astrae questa via:

public static class ExceptionExtensions 
{ 
    public static Exception GetInnermostException(this Exception e) 
    { 
     if (e == null) 
     { 
      throw new ArgumentNullException("e"); 
     } 

     while (e.InnerException != null) 
     { 
      e = e.InnerException; 
     } 

     return e; 
    } 
} 
1

A volte si potrebbe avere molte eccezioni interne (molte eccezioni gorgogliare). Nel qual caso si potrebbe desiderare di fare:

List<Exception> es = new List<Exception>(); 
while(e.InnerException != null) 
{ 
    es.add(e.InnerException); 
    e = e.InnerException 
} 
0

Non proprio una riga ma quasi:

 Func<Exception, Exception> last = null; 
     last = e => e.InnerException == null ? e : last(e.InnerException); 
108

Credo Exception.GetBaseException() fa la stessa cosa di queste soluzioni. http://msdn.microsoft.com/en-us/library/system.exception.getbaseexception(v=vs.71).aspx

Caveat: Da vari commenti abbiamo capito che non sempre letteralmente fare la stessa cosa, e in alcuni casi la soluzione/iterazione ricorsiva ti porterà ulteriormente. È di solito l'eccezione più interna, che è deludentemente incoerente, grazie a determinati tipi di eccezioni che sostituiscono il valore predefinito. Tuttavia, se rilevi tipi specifici di eccezioni e assicuri ragionevolmente che non sono strane (come AggregateException), allora mi aspetterei che ottenga l'eccezione legittima/iniziale più antica.

+3

+1 Non c'è niente come sapere il BCL per evitare soluzioni contorte. Chiaramente, questa è la risposta più corretta. – Jay

+3

Per qualche motivo, 'GetBaseException()' non ha restituito la prima eccezione di root. – Tarik

+4

Glenn McElhoe ha sottolineato che in effetti GetBaseException() non sempre fa ciò che MSDN suggerisce che possiamo aspettarci in generale (che "l'eccezione è la causa principale di una o più eccezioni successive"). In AggregateException è limitato all'eccezione più bassa dello stesso tipo e forse ce ne sono altri. –

1

In effetti è così semplice, si potrebbe usare Exception.GetBaseException()

Try 
     //Your code 
Catch ex As Exception 
     MessageBox.Show(ex.GetBaseException().Message, My.Settings.MsgBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); 
End Try 
+0

Grazie per la soluzione! Poche persone pensano in VB: D –

8

So che questo è un vecchio post, ma sono sorpreso nessuno ha chiesto GetBaseException() che è un metodo della classe Exception:

catch (Exception x) 
{ 
    var baseException = x.GetBaseException(); 
} 

Questo è stato in giro da. NET 1.1. Documentazione qui: http://msdn.microsoft.com/en-us/library/system.exception.getbaseexception(v=vs.71).aspx

+1

In realtà tale risposta è stata data - era sorprendente che nessuno l'avesse suggerito prima però. –

+0

Questa dovrebbe essere la risposta giusta! –

12

Il looping attraverso InnerExceptions è l'unico modo affidabile.

Se l'eccezione rilevata è un'eccezione Aggregate, quindi GetBaseException() restituisce solo l'eccezione AggregateException più interna.

http://msdn.microsoft.com/en-us/library/system.aggregateexception.getbaseexception.aspx

+1

Buona presa. Grazie - spiega i commenti sopra, in cui quella risposta non funzionava per alcune persone. Ciò rende chiaro che la descrizione generale di MSDN della "causa principale di una o più eccezioni successive" non è sempre quella che si assume poiché le classi derivate potrebbero sovrascriverle. La vera regola sembra essere che tutte le eccezioni in una catena di eccezioni "concordano" su GetBaseException() e restituiscono lo stesso oggetto, che sembra essere il caso di AggregateException. –

0

Devi loop, e di dover ciclo, è più pulito di spostare l'anello in una funzione separata.

Ho creato un metodo di estensione per affrontare questo. Restituisce un elenco di tutte le eccezioni interne del tipo specificato, inseguendo Exception.InnerException e AggregateException.InnerExceptions.

Nel mio particolare problema, rincorrere le eccezioni interne era più complicato del solito, perché le eccezioni venivano lanciate dai costruttori delle classi che venivano invocate attraverso la riflessione. L'eccezione che stavamo catturando aveva una InnerException di tipo TargetInvocationException, e le eccezioni che dovevamo effettivamente vedere erano sepolte in profondità nell'albero.

public static class ExceptionExtensions 
{ 
    public static IEnumerable<T> innerExceptions<T>(this Exception ex) 
     where T : Exception 
    { 
     var rVal = new List<T>(); 

     Action<Exception> lambda = null; 
     lambda = (x) => 
     { 
      var xt = x as T; 
      if (xt != null) 
       rVal.Add(xt); 

      if (x.InnerException != null) 
       lambda(x.InnerException); 

      var ax = x as AggregateException; 
      if (ax != null) 
      { 
       foreach (var aix in ax.InnerExceptions) 
        lambda(aix); 
      } 
     }; 

     lambda(ex); 

     return rVal; 
    } 
} 

L'utilizzo è piuttosto semplice. Se, per esempio, si desidera sapere se abbiamo incontrato un

catch (Exception ex) 
{ 
    var myExes = ex.innerExceptions<MyException>(); 
    if (myExes.Any(x => x.Message.StartsWith("Encountered my specific error"))) 
    { 
     // ... 
    } 
}