Cours complet pour apprendre à programmer en D


précédentsommairesuivant

43. Portées (scope)

Comme nous l'avons vu, les expressions qui doivent toujours être exécutées sont écrites dans le bloc finally, et les expressions qui doivent être exécutées quand il y a des conditions d'erreurs sont écrites dans des blocs catch.

On peut faire les observations suivantes sur l'utilisation de ces blocs :

  • catch et finally ne peuvent pas être utilisés sans un bloc try ;
  • certaines variables dont ces blocs ont besoin peuvent ne pas être accessibles à l'intérieur de ces blocs :

     
    Sélectionnez
    void foo(ref int r)
    {
        try {
            int nombreÀAjouter = 42;
     
            r += nombreÀAjouter;
            peutLever();
     
        } catch (Exception exc) {
            r -= nombreÀAjouter;       // ERREUR de compilation
        }
    }
  • Cette fonction modifie le paramètre référence puis annule cette modification quand une exception est levée. Malheureusement, nombreÀAjouter n'est accessible que dans le bloc try, là où il est défini.
    Note : ceci renvoie à la notion d'espace de noms ; comme pour la notion de durée de vie des objets, cela sera expliqué dans un chapitre ultérieur ;

  • écrire toutes les expressions potentiellement sans rapport dans un seul bloc finally en bas sépare ces expressions du code auquel elles sont liées.

Les instructions scope ont la même fonctionnalité que les blocs catch et finally, mais sont meilleures sur beaucoup d'aspects. Les trois instructions scope décrivent des expressions qui devraient être exécutées lorsqu'on quitte des portées :

  • scope(exit) : l'expression est toujours exécutée lorsqu'on quitte la portée, que ce soit normalement ou à cause d'une exception ;
  • scope(success) : l'expression n'est exécutée que si l'on quitte la portée normalement ;
  • scope(failure) : l'expression n'est exécutée que si l'on quitte la portée à cause d'une exception.

Même si ces instructions sont proches du mécanisme des exceptions, elles peuvent être utilisées sans bloc try-catch.

Comme exemple, réécrivons la fonction précédente avec une instruction scope(failure) :

 
Sélectionnez
void foo(ref int r)
{
    int nombreÀAjouter = 42;
 
    r += nombreÀAjouter;
    scope(failure) r -= nombreÀAjouter;
 
    peutLever();
}

L'instruction scope(failure) assure que l'expression r -= nombreÀAjouter sera exécutée si on sort de la portée de la fonction à cause d'une exception. Un avantage de scope(failure) est le fait que l'expression qui annule l'autre expression est écrite à côté de celle-ci.

Les instructions scope peuvent aussi être utilisées par blocs :

 
Sélectionnez
scope(exit) {
    // ... expressions ...
}

Voici une autre fonction qui teste ces trois instructions :

 
Sélectionnez
void test()
{
    scope(exit) writeln("en quittant 1");
 
    scope(success) {
        writeln("si succès 1");
        writeln("si succès 2");
    }
 
    scope(failure) writeln("si levée 1");
    scope(exit) writeln("en quittant 2");
    scope(failure) writeln("si levée 2");
 
    leveLaMoitieDuTemps();
}

Si aucune exception n'est levée, la sortie de la fonction n'inclut que les expressions scope(exit) et scope(success) :

 
Sélectionnez
en quittant 2
si succès 1
si succès 2
en quittant 1

Si une exception est levée, la sortie inclut les expressions scope(exit) et scope(failure) :

 
Sélectionnez
si levée 2
en quittant 2
si levée 1
en quittant 1
object.Exception@...: le message d'erreur

Comme on peut le constater, les blocs des instructions scope sont exécutés dans l'ordre inverse de celui dans lequel ils sont écrits dans le programme. C'est parce que du code situé plus bas peut dépendre de variables qui le précèdent. Exécuter les instructions scope dans l'ordre inverse permet d'annuler les effets de bords d'expressions précédentes dans un ordre cohérent.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+