Cela va être davantage une réponse conceptuelle à la raison pour laquelle ces avertissements peuvent exister même avec des approches d'essayer avec des ressources. Ce n'est malheureusement pas non plus le type de solution facile que vous espérez atteindre.
La récupération d'erreur ne peut pas échouer
finally
modélise un flux de contrôle post-transaction qui est exécuté, que la transaction réussisse ou échoue.
Dans le cas d'échec, finally
capture la logique qui est exécutée au milieu de la récupération d'une erreur, avant qu'elle ne soit complètement récupérée (avant d'atteindre notre catch
destination).
Imaginez le problème conceptuel qu'il présente pour rencontrer une erreur au milieu de la récupération d'une erreur.
Imaginez un serveur de base de données où nous essayons de valider une transaction, et il échoue à mi-chemin (par exemple, le serveur a manqué de mémoire au milieu). Maintenant, le serveur veut revenir à la transaction comme si de rien n'était. Imaginez cependant qu'il rencontre encore une autre erreur dans le processus de retour en arrière. Maintenant, nous finissons par avoir une transaction à moitié engagée dans la base de données - l'atomicité et la nature indivisible de la transaction sont désormais rompues, et l'intégrité de la base de données sera désormais compromise.
Ce problème conceptuel existe dans tous les langages traitant des erreurs, que ce soit C avec propagation manuelle de code d'erreur, ou C ++ avec exceptions et destructeurs, ou Java avec exceptions et finally
.
finally
ne peut pas échouer dans les langages qui le fournissent de la même manière que les destructeurs ne peuvent pas échouer en C ++ dans le processus de rencontrer des exceptions.
La seule façon d'éviter ce problème conceptuel et difficile est de s'assurer que le processus d'annulation des transactions et de libération des ressources au milieu ne peut pas rencontrer une exception / erreur récursive.
Donc, la seule conception sûre ici est une conception qui writer.close()
ne peut pas échouer. Il existe généralement des moyens dans la conception pour éviter les scénarios où de telles choses peuvent échouer au milieu de la récupération, ce qui le rend impossible.
C'est malheureusement le seul moyen - la récupération d'erreur ne peut pas échouer. Le moyen le plus simple de garantir cela est de rendre ces types de fonctions de "libération des ressources" et "d'effets secondaires inversés" incapables d'échouer. Ce n'est pas facile - une récupération correcte des erreurs est difficile et malheureusement difficile à tester. Mais le moyen d'y parvenir est de s'assurer que les fonctions qui "détruisent", "ferment", "arrêtent", "reculent", etc. ne peuvent pas rencontrer d'erreur externe dans le processus, car ces fonctions devront souvent être appelé au milieu de la récupération d'une erreur existante.
Exemple: enregistrement
Supposons que vous souhaitiez enregistrer des éléments dans un finally
bloc. Cela va souvent être un énorme problème à moins que la journalisation ne puisse échouer . L'enregistrement peut presque certainement échouer, car il peut vouloir ajouter plus de données au fichier, et cela peut facilement trouver de nombreuses raisons d'échouer.
Donc, la solution ici est de faire en sorte que toute fonction de journalisation utilisée dans les finally
blocs ne puisse pas être lancée à l'appelant (elle pourrait échouer, mais elle ne le lancera pas). Comment pourrions-nous faire cela? Si votre langue permet de lancer dans le contexte de enfin à condition qu'il y ait un bloc try / catch imbriqué, ce serait une façon d'éviter de lancer à l'appelant en avalant des exceptions et en les transformant en codes d'erreur, par exemple la journalisation peut être effectuée dans un autre processus ou thread qui peut échouer séparément et en dehors d'un déroulement de pile de récupération d'erreur existant. Tant que vous pouvez communiquer avec ce processus sans la possibilité de rencontrer une erreur, cela serait également sans danger pour les exceptions, car le problème de sécurité n'existe que dans ce scénario si nous lançons récursivement à partir du même thread.
Dans ce cas, nous pouvons nous en sortir avec un échec de journalisation à condition qu'il ne lance pas, car ne pas se connecter et ne rien faire n'est pas la fin du monde (il n'y a pas de fuite de ressources ou de réduction des effets secondaires, par exemple).
Quoi qu'il en soit, je suis sûr que vous pouvez déjà commencer à imaginer à quel point il est incroyablement difficile de vraiment sécuriser un logiciel. Il n'est peut-être pas nécessaire de rechercher cela au maximum dans tous les logiciels sauf les plus critiques. Mais il vaut la peine de prendre note de la façon de garantir véritablement la sécurité des exceptions, car même les auteurs de bibliothèques très générales tentent souvent ici et détruisent toute la sécurité des exceptions de votre application en utilisant la bibliothèque.
SomeFileWriter
Si SomeFileWriter
peut jeter à l'intérieur close
, alors je dirais qu'il est généralement incompatible avec la gestion des exceptions, sauf si vous n'essayez jamais de le fermer dans un contexte qui implique la récupération d'une exception existante. Si le code pour cela est hors de votre contrôle, nous pourrions être SOL mais il serait utile d'aviser les auteurs de ce problème flagrant de sécurité d'exception. Si c'est sous votre contrôle, ma principale recommandation est de veiller à ce que sa fermeture ne puisse pas échouer par tous les moyens nécessaires.
Imaginez si un système d'exploitation ne parvient pas à fermer un fichier. Désormais, tout programme qui tente de fermer un fichier à l'arrêt ne pourra pas s'arrêter . Que sommes-nous censés faire maintenant, simplement maintenir l'application ouverte et dans les limbes (probablement non), simplement divulguer la ressource de fichier et ignorer le problème (peut-être bien si ce n'est pas si critique)? La conception la plus sûre: faites en sorte qu'il soit impossible de ne pas fermer un fichier.