septembre
2010
Le dernier ticket sur la gestion d’exception (ou d’événement), malgré sa longueur, ne décrit pas un aspect délicat de celui-ci: la gestion d’exception dans les instructions de finalisation. En effet, si les instructions d’un bloc try
ont terminé sur le retour de la fonction (return
) ou sur une exception non capturée, il reste toujours des instructions de finalisation a exécuter. D’autres langages comme Java ont des règles peu satisfaisantes en ce qui concerne les exceptions levées à ce moment-là.
Pour comprendre la perspective de dodo sur ce point, il faut se rappeler un fait auquel je faisais allusion dans le dernier article: en dodo ce qui suit un bloc try
est l’équivalent d’un bloc finally
.
Voyons quelques scénarios possibles:
A. try { throw ExceptionA() } throw OtherException() B. try { return x } throw ExceptionB() C. try { throw ExceptionC() } return y
A. Masquage d’exception
Dans le cas A, les instructions de finalisation jettent une autre exception après que le bloc try
ait déjà fait de même. Dans ce cas les instructions de finalisation s’interrompent et la fonction jette une exception. En Java, l’exception jetée serait OtherException mais ce n’est pas une bonne solution. En effet, l’important est avant tout ce qui est arrivé auparavant à l’intérieur du bloc try
. Dodo, pour sa part, retourne l’exception ExceptionA. Mais ce n’est que le premier événement de la chaîne d’événements et l’on peut aussi obtenir la valeur de OtherException dans le code appelant.
Voici un résumé de l’exécution:
1. try { 2. throw ExceptionA()
Ici le contrôle échappe du bloc try, et continue dans les instructions de finalisation:
3. throw OtherException()
Maintenant les instructions de finalisation ne peuvent plus continuer.
La fonction termine avec une exception, retournant la chaîne d’événements:
ExceptionA, OtherException.
Le code appelant voit ExceptionA en tête de la chaîne d’événements.
B. Exception piégée par un return
Dans le cas B, le bloc try
termine en retournant une valeur x. Dans ce cas on pourrait argumenter que l’exception levée dans les instructions de finalisation est plus importante que la valeur de retour, mais ce n’est pas l’approche de dodo. En dodo, l’exécution des instructions de finalisation s’interrompt à l’exception et la fonction retourne la valeur x. Cependant, lors de la compilation il y a un message d’avertissement si les exceptions levées par le code de finalisation ne sont pas gérées dans un bloc catch
alors que le bloc try
contient un return
.
Pour éviter l’avertissement il faut gérer les cas exceptionnels dans les instructions de finalisation. Notez tout de même que dodo ne permet pas d’altérer le résultat final, qui est le retour de x à la fin de la fonction.
Un résumé de l’exécution pour cas B:
1. try { 2. return x
Ici le contrôle échappe du bloc try, et continue dans les instructions de finalisation:
3. throw ExceptionB()
Maintenant les instructions de finalisation ne peuvent plus continuer.
Cependant le résultat de la fonction était déjà décidé avant, lorsque return
a été appelé.
L’exception ExceptionB ne sort donc pas de la fonction. On dit qu’elle est piégée.
C. Instruction return tardive
Dans le cas C, les instructions de finalisation appellent return
alors qu’une exception a été levée avant:
try { throw ExceptionC() } return y
L’exception gagne, et la fonction termine en exception. Mais la valeur de retour est ajoutée à la chaîne d’événements. Le code appelant voit donc:
ExceptionC, return(y).
On peut aussi envisager un cas D où le bloc try
et le code de finalisation tous deux retournent une valeur. Dans ce cas la première valeur est retournée et le second return
est piégé. Cela ne génère pas de message d’avertissement, bien que ce soit bien entendu peu recommandé.