La differenza tra try / catch / throw e try / catch (e) / throw e


103

Qual è la differenza tra

try { }
catch
{ throw; }

e

try { }
catch(Exception e)
{ throw e;}

?

E quando dovrei usare l'uno o l'altro?

Risposte:


151

Le costruzioni

try { ... }
catch () { ... } /* You can even omit the () here */

try { ... }
catch (Exception e) { ... }

sono simili in quanto entrambi cattureranno ogni eccezione lanciata all'interno del tryblocco (e, a meno che tu non stia semplicemente usando questo per registrare le eccezioni, dovrebbero essere evitati ). Ora guarda questi:

try { ... }
catch ()
{
    /* ... */
    throw;
}

try { ... }
catch (Exception e)
{
    /* ... */
    throw;
}

try { ... }
catch (Exception e)
{
    /* ... */
    throw e;
}

Il primo e il secondo blocco try-catch sono ESATTAMENTE la stessa cosa, semplicemente rilanciano l'eccezione corrente, e quell'eccezione manterrà la sua "sorgente" e la traccia dello stack.

Il terzo blocco try-catch è diverso. Quando lancia l'eccezione, cambierà l'origine e la traccia dello stack, in modo che sembri che l'eccezione sia stata lanciata da questo metodo, da quella stessa riga throw esul metodo contenente quel blocco try-catch.

Quale dovresti usare? Dipende davvero da ogni caso.

Supponiamo che tu abbia una Personclasse con un .Save()metodo che la persisterà in un database. Supponiamo che la tua applicazione esegua il Person.Save()metodo da qualche parte. Se il tuo DB rifiuta di salvare la persona, allora .Save()genererà un'eccezione. Dovresti usare throwo throw ein questo caso? Beh, dipende.

Quello che preferisco è fare:

try {
    /* ... */
    person.Save();
}
catch(DBException e) {
    throw new InvalidPersonException(
       "The person has an invalid state and could not be saved!",
       e);
}

Ciò dovrebbe inserire DBException come "eccezione interna" dell'eccezione più recente generata. Quindi, quando controlli questa InvalidPersonException, la traccia dello stack conterrà informazioni sul metodo Save (che potrebbe essere sufficiente per risolvere il problema), ma hai ancora accesso all'eccezione originale se ne hai bisogno.

Come osservazione finale, quando ti aspetti un'eccezione, dovresti davvero catturare Exceptionquell'eccezione specifica e non generale , ovvero, se ti aspetti un'eccezione InvalidPersonException dovresti preferire:

try { ... }
catch (InvalidPersonException e) { ... }

per

try { ... }
catch (Exception e) { ... }

In bocca al lupo!


34

Il primo conserva la traccia dello stack mentre il secondo lo reimposta. Ciò significa che se usi il secondo approccio la traccia dello stack dell'eccezione partirà sempre da questo metodo e perderai la traccia dell'eccezione originale che potrebbe essere disastrosa per qualcuno che legge i log delle eccezioni poiché non scoprirà mai la causa originale dell'eccezione .

Il secondo approccio potrebbe essere utile quando si desidera aggiungere ulteriori informazioni alla traccia dello stack, ma viene utilizzato in questo modo:

try
{
    // do something
}
catch (Exception ex)
{
    throw new Exception("Additional information...", ex);
}

C'è un post sul blog che discute le differenze.


Bene, è fantastico sapere!
Myles

quindi perché usare il secondo allora? è meglio usare solo il primo?
Karim

1
Il secondo è utile quando è necessario controllare eccezioni specifiche - viene in mente OutOfRangeException - o è necessario registrare il messaggio, ecc. Il primo sembra essere un gestore di eccezioni jolly simile a provare {} catch (...) {} in c ++.
Salva l'

1
David, questo si applica solo alla parte di cattura (eccezione e) . E questo è separato dal throwvs throw e.
Henk Holterman

6

Dovresti usare

try { }
catch(Exception e)
{ throw }

se vuoi fare qualcosa con l'eccezione prima di lanciarla nuovamente (logging per esempio). Il lancio solitario preserva la traccia dello stack.


e cosa succederà se sostituisco il "lancio" qui con una "e"?
Karim

5

La differenza tra un catch senza parametri e a catch(Exception e)è che ottieni un riferimento all'eccezione. Dalla versione 2 del framework le eccezioni non gestite sono racchiuse in un'eccezione gestita, quindi l'eccezione senza parametri non è più utile per nulla.

La differenza tra throw;e throw e;è che il primo viene utilizzato per rilanciare le eccezioni e il secondo viene utilizzato per generare un'eccezione appena creata. Se usi la seconda per rilanciare un'eccezione, la tratterà come una nuova eccezione e sostituirà tutte le informazioni sullo stack da dove sono state originariamente lanciate.

Quindi, ti invitiamo a non utilizzare nessuna delle alternative nella domanda. Non dovresti usare il catch senza parametri e dovresti usare throw;per rilanciare un'eccezione.

Inoltre, nella maggior parte dei casi è necessario utilizzare una classe di eccezione più specifica rispetto alla classe base per tutte le eccezioni. Dovresti cogliere solo le eccezioni che prevedi.

try {
   ...
} catch (IOException e) {
   ...
   throw;
}

Se vuoi aggiungere delle informazioni quando rilanci l'eccezione, crei una nuova eccezione con l'eccezione originale come eccezione interna per preservare tutte le informazioni:

try {
   ...
} catch (IOException e) {
   ...
   throw new ApplicationException("Some informative error message", e);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.