web-dev-qa-db-fra.com

L'essai / enfin (sans le catch) fera-t-il exception?

Je suis presque certain que la réponse est OUI. Si j'utilise un bloc Try Final mais que je n'utilise pas de bloc Catch, toutes les exceptions bouillonneront. Correct?

Des réflexions sur la pratique en général?

Seth

104
Seth Spearman

Oui, absolument. En supposant que votre bloc finally ne lève pas d'exception, bien sûr, auquel cas cela "remplacera" effectivement celui qui a été lancé à l'origine.

117
Jon Skeet

Des réflexions sur la pratique en général?

Oui. Attention. Lorsque votre bloc enfin est en cours d'exécution, il est tout à fait possible qu'il s'exécute parce que ne exception non gérée et inattendue a été levée. Cela signifie que quelque chose est cassé, et quelque chose de complètement inattend pourrait se produire.

Dans cette situation, il est possible que vous ne deviez pas exécuter du code dans les blocs enfin. Le code dans le bloc finally peut être construit pour supposer que les sous-systèmes dont il dépend sont sains, alors qu'en fait ils pourraient être profondément brisés. Le code dans le bloc enfin pourrait aggraver les choses.

Par exemple, je vois souvent ce genre de chose:

DisableAccessToTheResource();
try
{
    DoSomethingToTheResource();
}
finally
{
    EnableAccessToTheResource();
}

L'auteur de ce code pense "je fais une mutation temporaire à l'état du monde; j'ai besoin de restaurer l'état à ce qu'il était avant d'être appelé". Mais réfléchissons à toutes les façons dont cela pourrait mal tourner.

Premièrement, l'accès à la ressource peut déjà être désactivé par l'appelant; dans ce cas, ce code le réactivera, peut-être prématurément.

Deuxièmement, si DoSomethingToTheResource lève une exception, est-ce la bonne chose à faire pour permettre l'accès à la ressource ??? Le code qui gère la ressource est cassé de façon inattendue. Ce code dit, en effet "si le code de gestion est cassé, assurez-vous qu'un autre code peut appeler ce code cassé dès que possible, afin qu'il puisse échouer horriblement aussi." Cela semble être une mauvaise idée.

Troisièmement, si DoSomethingToTheResource lève une exception, alors comment savoir si EnableAccessToTheResource ne lève pas également une exception? Quelle que soit la gravité subie, l'utilisation de la ressource peut également affecter le code de nettoyage, auquel cas l'exception d'origine sera perdue et le problème sera plus difficile à diagnostiquer.

J'ai tendance à écrire du code comme celui-ci sans utiliser de blocs try-finally:

bool wasDisabled = IsAccessDisabled();
if (!wasDisabled)
    DisableAccessToTheResource();
DoSomethingToTheResource();
if (!wasDisabled)
    EnableAccessToTheResource();

Maintenant, l'État n'est pas muté, sauf s'il doit l'être. Maintenant, l'état de l'appelant n'est pas dérangé. Et maintenant, si DoSomethingToTheResource échoue, nous ne réactivons pas l'accès. Nous supposons que quelque chose est profondément cassé et ne risque pas d'aggraver la situation en essayant de continuer à exécuter du code. Laissez l'appelant régler le problème, s'il le peut.

Alors, quand est-ce une bonne idée d'exécuter un bloc enfin? Tout d'abord, lorsque l'exception est attendue. Par exemple, vous pouvez vous attendre à ce qu'une tentative de verrouillage d'un fichier échoue, car quelqu'un d'autre l'a verrouillé. Dans ce cas, il est logique d'attraper l'exception et de la signaler à l'utilisateur. Dans ce cas, l'incertitude sur ce qui est brisé est réduite; il est peu probable que les choses empirent en nettoyant.

Deuxièmement, lorsque la ressource que vous nettoyez est une ressource système rare. Par exemple, il est logique de fermer un descripteur de fichier dans un bloc finally. (Une "utilisation" n'est bien sûr qu'une autre façon d'écrire un bloc try-finally.) Le contenu du fichier est peut-être corrompu, mais vous ne pouvez rien y faire maintenant. Le descripteur de fichier va être fermé à terme, donc il pourrait aussi bien être plus tôt que tard.

57
Eric Lippert