With, Goto et Label, le débat !

Embarcadero organise une série de débats entre développeurs sur l'utilisation de fonctionnalités de Delphi et du langage Pascal. Le mois dernier on a parlé de la procédure FreeAndNil() pour libérer de la mémoire allouée et remettre la valeur de la variable à nil. Ce mois-ci c'est encore plus chaud !

Aujourd'hui se pose la question d'utiliser ou pas les instructions "with", "goto" et "label" sur laquelle même David I est intervenu.

Autant il m'arrive d'utiliser "with" avec parcimonie et ne pas en recommander l'usage courant, autant pour "goto" et "label", je n'ai pas dû m'en servir depuis bien 30 ans et jamais dans Delphi au point d'oublier d'en parler quand je propose une formation au langage.

L'instruction "with" en langage Pascal

"With" permet de regrouper les actions sur un objet ou une structure en modifiant ses propriétés ou appelant ses méthodes sans avoir à repréciser l'identifiant concerné.

with Bidule do begin
    Valeur1 := 5;
    Valeur2 := 10;
    CalculeResultat;
end;

est un équivalent de

Bidule.Valeur1 := 5;
Bidule.Valeur2 := 10;
Bidule.CalculeResultat;

Ca n'a aucun sens d'écrire juste

With Bidule do
    Valeur1 := 5;

mais pourtant il arrive qu'on retrouve ce genre de choses. C'est une façon d'écrire disponible dans d'autres langages et comme pour la précision de "Self" dans la plupart des cas ça n'a aucun sens en Pascal.

Là où ça se complique c'est quand on imbrique des "with" ou qu'on l'utilise dans les méthodes d'une classe sur une classe ayant des propriétés ou méthodes équivalentes. Un changement dans la classe utilisée sur le "with" (comme le retrait d'une propriété ou méthode) peut avoir des conséquences invisibles lors de la compilation du code si ces éléments modifiés ont un équivalent disponible dans la classe à l'intérieur de laquelle on travaille.

Prenons par exemple deux classes ayant un "Caption". Dans une méthode de la première on fait un "with" pour alimenter le "Caption" de la seconde, mais manque de bol celui-ci est renommé en "Text". La modification effectuée sera donc le "Caption" de la première classe et non le "Text" de la seconde comme prévu lors de la compilation suivante du projet.

Rien n'apparaîtra à la compilation puisque syntaxiquement il n'y a pas de soucis. Sans tout tester / déboguer systématiquement on s'expose à des effets colatéraux.

En revanche comme l'a rappelé Paul Toth sur un forum, le compilateur optimise les appels lorsqu'on utilise "with" sur un pointeur provenant d'autre chose qu'une variable. Il faut mieux écrire 

with Variable.Propriété.Function do begin
    Truc := 10;
    Bidule := 20;
end;

que

Variable.Propriété.Function.Truc := 10;
Variable.Propriété.Function.Bidule := 20;

puisque dans le second cas on rappelle la fonction mais on peut aussi stocker cet appel dans une variable locale et ça restera à la fois optimisé et plus lisible:

var fct := Variable.Propriété.Function
fct.Truc := 10;
fct.Bidule := 20;

L'utilisation des variables en ligne avec autodéclaration grâce à l'inférence de type étant disponible depuis la version 10.3 Rio de Delphi. Dans les versions antérieures il faudra déclarer la variable dans le bloc de déclaration de la procédure/fonction dans laquelle on se trouve.

Donc usage de "with" autorisé, mais avoir conscience de ses limites et des risques que ça implique.

L'instruction "goto" en langage Pascal

Pour moi "goto" est plus litigueux : c'est un truc hérité du Basic et de l'époque où on n'avais pas de procédures ou fonctions pour externaliser du code.

Goto est une instruction de branchement vers un label qui aura été déclaré au préalable (ça reste du Pascal : on n'utilise que ce qui est déjà déclaré quelque part).

Très pratique en programmation séquentielle (voire indispensable pour sortir de boucles ou simuler des blocs de code appelés d'un ou plusieurs endroits), je n'en voit pas l'intérêt en programmation procédurale ou événementielle. Cette instruction est une bidouille pour sortir d'un flux d'instructions.

Ok, de nos jours on utilise des booléens et des tests pour sauter sur un bloc de code ou passer dedans. Avant on faisait un goto vers l'instruction suivante. Les deux se valent dans l'idée et en terme de compilateur ça doit se ressembler puisque le "if...then...else..." génère des sauts en langage machine une fois la condition évaluée. C'est à dire ce qu'on aurait écrit avec des GOTO !

Reste que ce n'est pas parce que les compilateur sle font qu'on doit le faire aussi.

J'estime qu'avec les éditeurs de codes, une indentation et la coloration syntaxique on ne doit pas avoir besoin d'ajouter des sauts pas forcément maintenables ou lisibles dans nos codes sources.

Nettoyer le langage en retirant des instructions historiques ?

L'autre question qui se pose est le retrait des instructions du langage pour une version ultérieure de Delphi, ajouter des warnings ou des alertes de déprécation.

Là je suis plutôt dans le camp de ceux qui disent non : ça doit rester pour conserver une compatibilité ascendante la plus grande possible avec du code d'il y a 30 ans, même si on peut faire du refactoring et changer ces fichiers sources pour les moderniser.

A la limite, si on veut être puriste, ajouter des warnings dans les logiciels d'analyse du code, mais je ne suis pas pour que l'interpréteur de code de Delphi y touche et encore moins les compilateurs.

Le duo "goto" / "label" existe depuis 50 ans (la création du langage Pascal), rien ne justifie de couper ces instructions dans une implémentation comme Delphi alors que ça ne le serait pas ailleurs (notamment dans des logiciels utilisant du Pascal interprété et ne proposant pas de procédures/fonctions dans ses macros).