La différence entre try / catch / throw et try / catch (e) / throw e

103

Quelle est la différence entre

try { }
catch
{ throw; }

et

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

?

Et quand dois-je utiliser l'un ou l'autre?

Karim
la source

Réponses:

151

Les constructions

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

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

sont similaires en ce que les deux intercepteront chaque exception lancée à l'intérieur du trybloc (et, à moins que vous ne l'utilisiez simplement pour consigner les exceptions, cela devrait être évité ). Maintenant, regardez-les:

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

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

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

Les premier et deuxième blocs try-catch sont exactement la même chose, ils relancent simplement l'exception courante, et cette exception conservera sa "source" et la trace de la pile.

Le troisième bloc try-catch est différent. Quand il lève l'exception, il changera la source et la trace de la pile, de sorte qu'il apparaîtra que l'exception a été levée à partir de cette méthode, à partir de cette même ligne throw esur la méthode contenant ce bloc try-catch.

Lequel devriez-vous utiliser? Cela dépend vraiment de chaque cas.

Supposons que vous ayez une Personclasse avec une .Save()méthode qui la conservera dans une base de données. Disons que votre application exécute lePerson.Save() méthode quelque part. Si votre base de données refuse de sauvegarder la personne, .Save()elle lèvera une exception. Devriez-vous utiliser throwou throw edans ce cas? En fait ça dépend.

Ce que je préfère, c'est faire:

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

Cela devrait placer DBException comme "exception interne" de la nouvelle exception en cours de levée. Ainsi, lorsque vous inspectez cette InvalidPersonException, la trace de la pile contiendra des informations sur la méthode Save (cela peut être suffisant pour que vous résolviez le problème), mais vous avez toujours accès à l'exception d'origine si vous en avez besoin.

En guise de remarque finale, lorsque vous attendez une exception, vous devriez vraiment attraper cette exception spécifique, et non une exception générale Exception, c'est-à-dire que si vous attendez une exception InvalidPersonException, vous devriez préférer:

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

à

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

Bonne chance!

Bruno Reis
la source
34

Le premier préserve la trace de la pile tandis que le second la réinitialise. Cela signifie que si vous utilisez la deuxième approche, la trace de pile de l'exception commencera toujours à partir de cette méthode et vous perdrez la trace d'exception d'origine, ce qui pourrait être désastreux pour quelqu'un qui lit les journaux d'exceptions car il ne trouvera jamais la cause d'origine de l'exception .

La deuxième approche peut être utile lorsque vous souhaitez ajouter des informations supplémentaires à la trace de la pile, mais elle est utilisée comme ceci:

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

Il y a un blog après discuter des différences.

Darin Dimitrov
la source
Eh bien, c'est génial de savoir!
Myles
alors pourquoi utiliser le second alors? vaut-il mieux n'utiliser que le premier?
Karim
1
Le second est pratique lorsque vous devez vérifier des exceptions spécifiques - OutOfRangeException vient à l'esprit - ou devez consigner le message, etc. Le premier semble être un gestionnaire d'exceptions générique similaire à try {} catch (...) {} en c ++.
3Dave
1
David, cela ne s'applique qu'à la partie catch (Exception e) . Et qui est séparé de throwvs throw e.
Henk Holterman
6

Tu devrais utiliser

try { }
catch(Exception e)
{ throw }

si vous voulez faire quelque chose avec l'exception avant de le relancer (journalisation par exemple). Le lancer solitaire préserve la trace de la pile.

Otávio Décio
la source
et que se passera-t-il si je remplace le «lancer» par un «lancer e»?
Karim
5

La différence entre une capture sans paramètre et une catch(Exception e)est que vous obtenez une référence à l'exception. À partir de la version 2 du framework, les exceptions non gérées sont encapsulées dans une exception gérée, de sorte que l'exception sans paramètre n'est plus utile pour rien.

La différence entre throw;etthrow e; est que le premier est utilisé pour renvoyer des exceptions et le second est utilisé pour lever une exception nouvellement créée. Si vous utilisez le second pour renvoyer une exception, il la traitera comme une nouvelle exception et remplacera toutes les informations de pile d'où elles ont été lancées à l'origine.

Donc, vous ne devez utiliser aucune des alternatives de la question. Vous ne devez pas utiliser la capture sans paramètre et vous devez l'utiliser throw;pour renvoyer une exception.

En outre, dans la plupart des cas, vous devez utiliser une classe d'exception plus spécifique que la classe de base pour toutes les exceptions. Vous ne devriez attraper que les exceptions que vous prévoyez.

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

Si vous souhaitez ajouter des informations lors de la relance de l'exception, vous créez une nouvelle exception avec l'exception d'origine en tant qu'exception interne pour préserver toutes les informations:

try {
   ...
} catch (IOException e) {
   ...
   throw new ApplicationException("Some informative error message", e);
}
Guffa
la source