Question

J'ai vu le code suivant à plusieurs reprises:

try
{
    ... // some code
}
catch (Exception ex)
{
    ... // Do something
    throw new CustomException(ex);

    // or
    // throw;

    // or
    // throw ex;
}

Pouvez-vous s'il vous plaît expliquer le but de re-lancer une exception? Est-ce que cela suit un modèle / une pratique exemplaire dans la gestion des exceptions? (J'ai lu quelque part qu'il s'appelle "motif appelant"?)

Était-ce utile?

La solution

Renvoyer la même exception est utile si vous voulez, par exemple, consigner l'exception, mais ne pas la gérer.

Le lancement d'une nouvelle exception qui englobe l'exception capturée est bon pour l'abstraction. Par exemple, votre bibliothèque utilise une bibliothèque tierce qui lève une exception que les clients de votre bibliothèque ne devraient pas connaître. Dans ce cas, vous l'enroulez dans un type d'exception plus natif de votre bibliothèque et le lancez à la place.

Autres conseils

En fait, il y a une différence entre

throw new CustomException(ex);

et

throw;

Le second préservera les informations de la pile.

Mais parfois, vous souhaitez que l'exception soit plus "conviviale". Dans votre domaine d'application, au lieu de laisser DatabaseException atteindre votre interface graphique, vous allez générer votre exception personnalisée qui contient l'exception d'origine.

Par exemple:

try
{

}
catch (SqlException ex)
{
    switch  (ex.Number) {
        case 17:
        case 4060:
        case 18456:
           throw new InvalidDatabaseConnectionException("The database does not exists or cannot be reached using the supplied connection settings.", ex);
        case 547:
            throw new CouldNotDeleteException("There is a another object still using this object, therefore it cannot be deleted.", ex);
        default:
            throw new UnexpectedDatabaseErrorException("There was an unexpected error from the database.", ex);
    } 
}

Parfois, vous souhaitez masquer les détails d'implémentation d'une méthode ou améliorer le niveau d'abstraction d'un problème afin qu'il soit plus significatif pour l'appelant d'une méthode. Pour ce faire, vous pouvez intercepter l’exception d’origine et substituer une exception personnalisée mieux adaptée pour expliquer le problème.

Prenons par exemple une méthode qui charge les informations de l’utilisateur demandé à partir d’un fichier texte. La méthode suppose qu’il existe un fichier texte portant l’ID utilisateur et un suffixe «.data». Lorsque ce fichier n’existe pas, il n’est pas logique de lancer une exception FileNotFoundException car le fait que les informations de chaque utilisateur soient stockées dans un fichier texte est un détail d’implémentation interne à la méthode. Donc, cette méthode pourrait plutôt encapsuler l'exception d'origine dans une exception personnalisée avec un message explicatif.

Contrairement au code affiché, la meilleure pratique consiste à conserver l'exception d'origine en la chargeant en tant que propriété InnerException de votre nouvelle exception. Cela signifie qu'un développeur peut toujours analyser le problème sous-jacent si nécessaire.

Lorsque vous créez une exception personnalisée, voici une liste de contrôle utile:

• Trouvez un bon nom qui explique pourquoi l'exception a été levée et assurez-vous qu'il se termine par le mot "Exception".

• Assurez-vous d'implémenter les trois constructeurs d'exceptions standard.

• Assurez-vous de marquer votre exception avec l'attribut Serializable.

• Assurez-vous d'implémenter le constructeur de désérialisation.

• Ajoutez toutes les propriétés d'exception personnalisées qui pourraient aider les développeurs à mieux comprendre et gérer votre exception.

• Si vous ajoutez des propriétés personnalisées, veillez à implémenter et à redéfinir GetObjectData pour sérialiser vos propriétés personnalisées.

• Si vous ajoutez des propriétés personnalisées, remplacez la propriété Message afin de pouvoir ajouter vos propriétés au message d'exception standard.

• N'oubliez pas de joindre l'exception d'origine à l'aide de la propriété InnerException de votre exception personnalisée.

En général, vous interceptez et relancez pour l'une des deux raisons, en fonction de l'architecture du code dans une application.

Au cœur d’une application, vous attrapez et relancez de nouveau pour traduire une exception en quelque chose de plus significatif. Par exemple, si vous écrivez une couche d'accès aux données et utilisez des codes d'erreur personnalisés avec SQL Server, vous pouvez traduire SqlException en éléments tels que ObjectNotFoundException. Cela est utile car (a) il est plus facile pour les appelants de gérer des types d’exception spécifiques et (b), car il empêche les détails d’implémentation de cette couche, tels que le fait que vous utilisez SQL Server pour la fuite de persistance dans d’autres couches. vous permet de changer plus facilement les choses à l'avenir.

Aux limites des applications, il est courant d’attraper et de relancer sans traduire une exception afin de pouvoir enregistrer les détails de celle-ci, ce qui facilite le débogage et le diagnostic des problèmes réels. Idéalement, vous souhaitez publier une erreur quelque part que l'équipe des opérations peut facilement surveiller (par exemple, le journal des événements) ainsi que quelque part qui fournit un contexte indiquant l'emplacement de l'exception dans le flux de contrôle pour les développeurs (généralement le traçage).

Je peux penser aux raisons suivantes:

  • Conserver l'ensemble des types d'exceptions levés dans l'API afin que les appelants n'aient à se préoccuper que de l'ensemble d'exceptions corrigé. En Java, vous êtes pratiquement obligé de le faire, à cause du mécanisme des exceptions vérifiées.

  • Ajout de quelques informations de contexte à l'exception. Par exemple, au lieu de laisser le nu "enregistrement non trouvé" Si vous passez à partir de la base de données, vous voudrez peut-être la saisir et ajouter "... lors du traitement de la commande n ° XXX, à la recherche du produit YYY".

  • Effectuer des opérations de nettoyage - fermeture de fichiers, annulation de transactions, libération de descripteurs.

En règle générale, l'option "Faire quelque chose" implique soit de mieux expliquer l’exception (par exemple, de l’envelopper dans une autre exception), soit de tracer des informations dans une source donnée.

Une autre possibilité est que, si le type d’exception ne contienne pas suffisamment d’informations pour savoir si une exception doit être interceptée, auquel cas le fait de l’examiner en le renseignant fournira plus d’informations.

Cela ne veut pas dire que la méthode est utilisée pour de bonnes raisons, mais elle est souvent utilisée lorsqu'un développeur pense que des informations de traçage peuvent être nécessaires ultérieurement, auquel cas vous obtenez le style try {} catch {throw;}. , ce qui n'est pas du tout utile.

Je pense que cela dépend de ce que vous essayez de faire avec l'exception.

Une bonne raison serait de consigner l'erreur en premier dans la capture, puis de la renvoyer à l'interface utilisateur pour générer un message d'erreur convivial avec la possibilité d'afficher un message "avancé / détaillé" plus détaillé. vue de l'erreur, qui contient l'erreur d'origine.

Une autre approche consiste à "recommencer". approche, par exemple, un nombre d’erreurs est conservé et, après un certain nombre de tentatives, c’est la seule fois où l’erreur est envoyée dans la pile (ceci est parfois fait pour l’accès à la base de données pour les appels de base de données expirant ou pour l’accès à des services Web sur des réseaux lents ).

Il y aura cependant d'autres raisons de le faire.

Pour votre information, voici une question connexe à propos de chaque type de re-lancer: Considérations sur les performances pour le lancement d'exceptions

Ma question porte sur "Pourquoi". nous rediffusons les exceptions et leur utilisation dans la stratégie de gestion des exceptions d'application.

Jusqu'à ce que je commence à utiliser EntLib ExceptionBlock, je les utilisais pour consigner les erreurs avant de les lancer. C'est un peu vilain quand on pense que j'aurais pu les gérer à ce moment-là, mais à ce moment-là, il était préférable de les laisser échouer méchamment dans UAT (après les avoir journalisées) plutôt que de couvrir un bogue qui circulait.

L’application détectera très probablement les exceptions rediffusées plus haut dans la pile d’appels et permet donc à ce dernier de les intercepter et de les traiter comme il convient. Il est assez courant pour une application d’avoir un gestionnaire d’exception de niveau supérieur qui enregistre ou rapporte les expections.

Une autre alternative est que le codeur était paresseux et que, au lieu d’attraper l’ensemble des exceptions qu’ils souhaitent gérer, ils ont tout capturé puis rejoué uniquement ceux qu’ils ne peuvent pas gérer.

Comme Rafal l’a mentionné, il arrive parfois que l’on convertisse une exception vérifiée en une exception non vérifiée ou en une exception vérifiée qui convient mieux à une API. Il y a un exemple ici:

http://radio-weblogs.com/0122027 /stories/2003/04/01/JavasCheckedExceptionsWereAMistake.html

Si vous considérez les exceptions comme une autre solution permettant d'obtenir un résultat de méthode , le renvoi d'une exception revient à insérer votre résultat dans un autre objet.

Et c’est une opération courante dans un monde non exceptionnel. Cela se produit généralement sur une bordure de deux couches d'application - lorsqu'une fonction du calque B appelle une fonction du calque C , elle transforme le code de C . résultat dans la forme interne de B .

A - appels - > B - appels - > C

Si ce n'est pas le cas, au niveau de la couche A qui appelle la couche B , un ensemble complet d'exceptions JDK sera géré. : -)

Comme le souligne également la réponse acceptée, la couche A peut même ne pas être au courant de l'exception de C .

Exemple

Couche A , servlet: récupère une image et ses méta-informations
Couche B , bibliothèque JPEG: collecte les balises DCIM disponibles pour analyser un fichier JPEG
Layer C , un DB simple: une classe lisant une chaîne de caractères enregistre à partir d'un fichier à accès aléatoire. Certains octets sont cassés, ainsi une exception indiquant "Ne peut pas lire la chaîne UTF-8 pour l'enregistrement" bibliographicCitation '"est générée.

Donc Un ne comprendra pas la signification de 'bibliographicCitation'. Ainsi, B doit traduire cette exception pour A en TagsReadingException qui enveloppe l'original.

LA PRINCIPALE RAISON de rediffuser les exceptions est de ne pas toucher à Call Stack afin que vous puissiez avoir une image plus complète de ce qui se passe et de la séquence des appels.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top