Domanda

Quali sono le migliori pratiche da prendere in considerazione quando la cattura di eccezioni e ri-buttare?Voglio fare in modo che il Exception oggetto InnerException e stack sono conservati.C'è una differenza tra i seguenti blocchi di codice nel modo in cui gestire questo?

try
{
    //some code
}
catch (Exception ex)
{
    throw ex;
}

Vs:

try
{
    //some code
}
catch
{
    throw;
}
È stato utile?

Soluzione

Per mantenere lo stack trace è attraverso l'uso di throw; Questo è valido

try {
  // something that bombs here
} catch (Exception ex)
{
    throw;
}

throw ex; è praticamente come la generazione di un'eccezione da questo punto, in modo che la traccia dello stack vorresti solo andare a dove si rilascia il throw ex; istruzione.

Mike è anche corretto, supponendo che l'eccezione consente di passare un'eccezione (che è raccomandato).

Karl Seguin ha un grande scrivere sulla gestione delle eccezioni nel suo fondamenti di programmazione e-book come pure, che è un grande letto.

Edit:Link funzionante per Fondamenti di Programmazione pdf.Basta cercare il testo di "eccezione".

Altri suggerimenti

Se si lancia una nuova eccezione con la prima eccezione è preservare l'analisi dello stack iniziale troppo..

try{
} 
catch(Exception ex){
     throw new MoreDescriptiveException("here is what was happening", ex);
}

In realtà, ci sono alcune situazioni in cui il throw statment non conserva lo StackTrace informazioni.Per esempio, nel codice riportato di seguito:

try
{
  int i = 0;
  int j = 12 / i; // Line 47
  int k = j + 1;
}
catch
{
  // do something
  // ...
  throw; // Line 54
}

Il StackTrace indica che la linea 54 sollevato l'eccezione, anche se è stato sollevato alla riga 47.

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.WithThrowIncomplete() in Program.cs:line 54
   at Program.Main(String[] args) in Program.cs:line 106

In situazioni come quella descritta sopra, ci sono due opzioni per preseve originale StackTrace:

Chiamando l'Eccezione.InternalPreserveStackTrace

Come è un metodo privato, deve essere richiamata tramite la riflessione:

private static void PreserveStackTrace(Exception exception)
{
  MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
    BindingFlags.Instance | BindingFlags.NonPublic);
  preserveStackTrace.Invoke(exception, null);
}

Mi ha anche i suoi svantaggi di basarsi su un metodo privato per preservare la StackTrace informazioni.Esso può essere modificato nelle versioni future di .NET Framework.Il codice di esempio di cui sopra, e la soluzione proposta qui di seguito è stato estratto dal Fabrice MARGUERIE weblog.

Chiamata Eccezione.SetObjectData

La tecnica qui sotto è stato suggerito da Anton Tykhyy come risposta a In C#, come posso rigenerare InnerException senza perdere traccia di stack domanda.

static void PreserveStackTrace (Exception e) 
{ 
  var ctx = new StreamingContext  (StreamingContextStates.CrossAppDomain) ; 
  var mgr = new ObjectManager     (null, ctx) ; 
  var si  = new SerializationInfo (e.GetType (), new FormatterConverter ()) ; 

  e.GetObjectData    (si, ctx)  ; 
  mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData 
  mgr.DoFixups       ()         ; // ObjectManager calls SetObjectData 

  // voila, e is unmodified save for _remoteStackTraceString 
} 

Anche se, ha il vantaggio di affidarsi a metodi pubblici solo dipende anche la seguente eccezione costruttore (di cui alcune eccezioni sviluppato da 3 ° parti non implementare):

protected Exception(
    SerializationInfo info,
    StreamingContext context
)

Nella mia situazione, ho dovuto scegliere il primo approccio, perché le eccezioni sollevate da un 3rd-party biblioteca stavo usando non implementare questo costruttore.

Quando si throw ex, si sta essenzialmente lancio di una nuova eccezione, e di perdere la traccia dello stack originale informazioni. throw è il metodo preferito.

La regola del pollice è quello di evitare la Cattura e Gettando la base Exception oggetto.Questo ti costringe a essere un po ' più intelligente sulle eccezioni;in altre parole, si dovrebbe avere un esplicito di cattura per un SqlException in modo che il vostro codice di gestione di non fare qualcosa di sbagliato con un NullReferenceException.

Nel mondo reale, però, la cattura di e la registrazione l'eccezione base è sempre una buona pratica, ma non dimenticate di camminare il tutto per ottenere qualsiasi InnerExceptions si potrebbe avere.

Nessuno ha spiegato la differenza tra ExceptionDispatchInfo.Capture( ex ).Throw() e una normale throw, qui è così.Tuttavia, alcune persone hanno notato il problema con throw.

Il procedimento completo per rigenerare una eccezione viene rilevata da usare ExceptionDispatchInfo.Capture( ex ).Throw() (disponibile solo da .Net 4.5).

Sotto ci sono i casi necessari per eseguire questo test:

1.

void CallingMethod()
{
    //try
    {
        throw new Exception( "TEST" );
    }
    //catch
    {
    //    throw;
    }
}

2.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        ExceptionDispatchInfo.Capture( ex ).Throw();
        throw; // So the compiler doesn't complain about methods which don't either return or throw.
    }
}

3.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch
    {
        throw;
    }
}

4.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        throw new Exception( "RETHROW", ex );
    }
}

Caso 1 e caso 2 vi darà una traccia dello stack in cui il codice sorgente numero di riga per il CallingMethod il metodo è il numero di riga dell' throw new Exception( "TEST" ) linea.

Tuttavia, il caso 3 vi darà una traccia dello stack in cui il codice sorgente numero di riga per il CallingMethod il metodo è il numero di riga dell' throw chiamata.Questo significa che se il throw new Exception( "TEST" ) la linea è circondata da altre operazioni, non avete idea di che numero di riga l'eccezione è stata effettivamente gettato.

Caso 4 è simile con il caso 2, perché il numero di riga dell'eccezione originale è conservato, ma non è un vero e proprio rilancio, perché cambia il tipo di eccezione originale.

Poche persone hanno perso un punto molto importante - 'passi' e 'buttare ex' possibile fare la stessa cosa ma non ti danno un pezzo fondamentale di informazioni che è la linea in cui si è verificato l'eccezione.

Si consideri il seguente codice:

static void Main(string[] args)
{
    try
    {
        TestMe();
    }
    catch (Exception ex)
    {
        string ss = ex.ToString();
    }
}

static void TestMe()
{
    try
    {
        //here's some code that will generate an exception - line #17
    }
    catch (Exception ex)
    {
        //throw new ApplicationException(ex.ToString());
        throw ex; // line# 22
    }
}

Quando si esegue un 'tiro' o 'buttare ex' si ottiene la traccia dello stack, ma la linea# sarà #22 così si riesce a capire che la linea era esattamente lanciare l'eccezione (a meno che non hai solo 1 o poche linee di codice in un blocco try).Per ottenere il previsto line #17 nel vostro eccezione dovrete lanciare una nuova eccezione con l'originale analisi dello stack dell'eccezione.

Si dovrebbe sempre usare "buttare";" rigenerare le eccezioni .NET,

Fare riferimento a questa, http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx

Fondamentalmente MSIL (CIL) ha due istruzioni - "passi" e "rigenerare":

  • C#'s "buttare " ex" viene compilato in MSIL del "buttare"
  • C#'s "buttare";" - in MSIL "rilancio"!

In pratica posso vedere il motivo per cui "buttare " ex", che sostituisce la traccia dello stack.

Si può anche utilizzare:

try
{
// Dangerous code
}
finally
{
// clean up, or do nothing
}

E le eventuali eccezioni generate saranno bolla al livello successivo, che li gestisce.

Mi sarebbe sicuramente usare di:

try
{
    //some code
}
catch
{
    //you should totally do something here, but feel free to rethrow
    //if you need to send the exception up the stack.
    throw;
}

Conservare il vostro stack.

FYI ho appena provato questa e la traccia dello stack riportato da 'buttare;' non è del tutto corretta analisi dello stack.Esempio:

    private void foo()
    {
        try
        {
            bar(3);
            bar(2);
            bar(1);
            bar(0);
        }
        catch(DivideByZeroException)
        {
            //log message and rethrow...
            throw;
        }
    }

    private void bar(int b)
    {
        int a = 1;
        int c = a/b;  // Generate divide by zero exception.
    }

L'analisi dello stack punti all'origine dell'eccezione correttamente riportato il numero di riga) ma il numero di riga riportata di foo() è la linea di tiro;dichiarazione, quindi non si può dire che le chiamate a un bar() ha causato l'eccezione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top