121 votes

Quel est l'intérêt de finalement dans une instruction try catch/except enfin

J'ai utilisé des variantes try-catch/except-finally dans de nombreuses langues depuis des années, aujourd'hui quelqu'un m'a demandé quel est l'intérêt de finally et je n'ai pas pu répondre.

En gros, pourquoi mettriez-vous une instruction dans finally plutôt que de la mettre après tout le bloc try-catch? Ou en d'autres termes, y a-t-il une différence entre les blocs de code suivants :

try{ //a}
catch {//b}
finally {//c}

try{//a}
catch{//b}
//c

ÉDIT :
LES GENS, je sais ce que finally fait, je l'utilise depuis des années, mais ma question est dans l'exemple ci-dessus, mettre //c dans finally semble redondant, non?

157voto

supercat Points 25534

Le but d'un bloc finally est de garantir que le code soit exécuté dans trois circonstances qui ne seraient pas très proprement gérées en utilisant uniquement des blocs "catch" :

  1. Si le code dans le bloc try sort via une sortie en cascade ou return
  2. Si le code dans un bloc catch relance l'exception attrapée, ou - accidentellement ou intentionnellement - lance une nouvelle exception.
  3. Si le code dans le bloc try rencontre une exception pour laquelle le try n'a pas de catch.

On pourrait copier le code finally avant chaque return ou throw, et envelopper les blocs catch dans leur propre try/catch pour permettre la possibilité d'une exception accidentelle, mais il est beaucoup plus facile de renoncer à tout cela et simplement utiliser un bloc finally.

Au fait, une chose que j'aimerais que les concepteurs de langages incluent serait un argument exception pour le bloc finally, pour gérer le cas où l'on a besoin de nettoyer après une exception mais souhaite quand même la remonter dans la pile d'appels (par exemple, on pourrait envelopper le code d'un constructeur dans une telle construction, et Dispose l'objet en cours de construction si le constructeur devait sortir avec une exception).

13voto

modu Points 666

Pour rendre les choses encore plus faciles à comprendre :

try{//a}
catch{//b}
//c

Dans le code ci-dessus, //c ne s'exécutera pas :

  • si vous utilisez "return" à l'intérieur du bloc try. **
  • si vous utilisez "return" à l'intérieur du bloc catch. **
  • si vous lancez une exception à l'intérieur du bloc catch.
  • si votre bloc try lance une exception qui ne peut pas être capturée par votre bloc catch.

Tandis que dans le code ci-dessous :

try{//a}
catch{//b}
finally{//c}

//c s'exécutera quoi qu'il arrive.

3voto

Matt Wilko Points 11348

Enfin vous permet d'exécuter toujours une section de code quel que soit ce qui se passe dans la section Try.

Par exemple, vous pourriez vouloir faire ceci (pseudo code)

Try
    OpenFile
    ReadSomeTextFromFile
Catch
    Une erreur s'est produite
Finally
    FermerLeFichierSiEncoreOuvert

Si le fichier s'ouvre correctement et que vous lisez quelque chose dedans, alors le bloc Finally ferme le fichier ouvert

Si l'ouverture du fichier ou la lecture du fichier échoue pour une raison quelconque, cela garantit que le fichier est fermé (s'il a effectivement été ouvert)

Si votre code n'avait pas de bloc Finally, il ressemblerait à ceci :

Try
    OpenFile
    ReadSomeTextFromFile
    CloseTheFileIfStillOpen
Catch
    Une erreur s'est produite - mais le fichier peut rester ouvert

Si une erreur se produit dans ReadSomeTextFromFile, une erreur se produit à ce moment-là et l'exécution du code passe au bloc Catch donc CloseTheFileIfStillOpen ne s'exécute pas et le fichier reste ouvert.

Réécrire le code dans le format de votre question modifiée :

Try
    OpenFile
    ReadSomeTextFromFile
Catch
    CloseTheFileIfStillOpen

Dans cet exemple, le fichier est seulement fermé si une erreur est levée, pas pendant l'exécution normale du code

Ce lien fournit un exemple non pseudo code (C#) de ce qui a été mentionné ci-dessus

1voto

Jan Turoň Points 6598

finally est un sucre syntaxique permettant de respecter le principe DRY dans le motif try-catch. Une exception est généralement lancée si le code de la bibliothèque n'a pas suffisamment d'informations pour gérer certains états et souhaite que le code client le résolve. Si vous n'avez pas de séparation entre le code bibliothèque-client, vous pouvez tout gérer avec des if au lieu de try.

Voyons une situation standard sans finally:

void myFunction() {
  var r = allocateResources();
  r.doSomething();
  if(somethingBadHappens) {
    freeResources(r);
    throw new Exception(CODE42);
  }
  r.doSomethingMore();
  freeResources(r);
}

Dans l'exemple ci-dessus, vous répétez freeResources() : il peut s'agir de plusieurs déclarations que vous devez répéter. Cela n'est pas optimal et le bloc finally est la solution pour un code propre :

void myFunction() {
  var r = allocateResources();
  try {
    r.doSomething();
    if(somethingBadHappens) throw new Exception(CODE42);
    r.doSomethingMore();
  }
  finally {
    freeResources(r);
  }
  happyFunction();
}

Réalisont maintenant trois niveaux d'abstraction :

  • A1 est le code bibliothèque fournissant la fonction allocateResources()
  • A2 est notre code fournissant myFunction, consommant A1
  • A3 est un code client consommant myFunction dans un bloc try-catch :

    function A3code() { try { myFunction(); doSomething(); } catch(Exception e) { // pas de ressources en attente ici Console.WriteLine(e); } }

Voyons maintenant ce qui peut se passer :

  • si allocateResources() lève une exception dans A1, nous ne savons pas comment la gérer dans A2 (le code A2 peut s'exécuter dans un environnement sans Console), nous déléguons donc la situation à A3 sans ajouter de code supplémentaire. Si une exception est levée ici, le bloc finally n'est pas exécuté, car finally est lié à try qui n'a pas été exécuté.
  • si somethingBadHappens dans le bloc try, la stack remonte jusqu'à A3 où la situation est gérée MAIS avant cela, le bloc finally est exécuté, donc nous n'avons pas besoin de le répéter s'il n'y a pas d'exceptions.
  • avant finally, nous pouvons ajouter un bloc catch et essayer de résoudre certaines exceptions potentielles de A1 qui peuvent apparaître dans l'appel des méthodes r.doSomething. En général, nous voulons gérer les exceptions le plus tôt possible pour rendre le code client (A3) plus confortable pour les programmeurs.
  • happyFunction() est exécuté uniquement si rien ne lance d'exception dans myFunction() (à l'intérieur ou à l'extérieur du bloc try).

Comme le souligne @supercat, le bloc finally est également exécuté si le bloc try se termine par un retour. Je vous suggère d'éviter cette mauvaise habitude et d'avoir un seul retour dans chaque fonction (peut-être quelques sorties prématurées au tout début des fonctions). Les raisons pour les fonctions à un seul retour sont les suivantes :

  1. Le code est plus lisible : vous regardez à la fin et vous voyez CE QUE la fonction retourne. Avec plusieurs retours, vous devez trouver tous les retours, inspecter tous les ifs, réfléchir quand les ifs sont satisfaits et seulement alors savoir ce que la fonction retourne.
  2. Le code peut être optimisé par les compilateurs, voir l'élimination de la copie.

La raison des multiples retours est d'éviter de nombreux ifs imbriqués, mais il existe d'autres techniques pour le résoudre. Les Exceptions sont une exception à cette règle.

0voto

paxafe Points 280

Le bloc enfin est exécuté même si une exception est lancée dans le bloc try. Par conséquent, par exemple, si vous avez ouvert un flux auparavant, vous voudrez peut-être fermer ce flux que qu'une exception soit lancée ou non. Le bloc enfin est utile pour ce genre de situation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X