Delphi RTL inclut un moteur d’expression très puissant, qui est l’un des fondements de l’architecture Live Bindings, mais peut être utilisé comme moteur distinct pour le traitement des expressions. Cet article de blog offre une introduction rapide au sujet.
Il existe de nombreux joyaux cachés dans le Delphi RTL. L’un d’eux est le moteur d’expression. Récemment, j’ai eu des conversations avec un développeur Delphi de longue date qui recherchait une fonctionnalité similaire, sans se rendre compte qu’elle était dans le produit depuis de nombreuses années. Je savais que j’avais écrit des démos et de la documentation alors j’ai passé un peu de temps à les chercher et j’en ai trouvé quelques-unes. Maintenant, ce sujet est très complexe et je ne pourrai pas le couvrir en entier, mais pour des scénarios simples, vous avez vraiment besoin de très peu de code pour analyser et traiter une expression.
Avant de commencer avec le vrai sujet, je voulais aussi mentionner que nous avons récemment fait surface cette fonctionnalité dans le nouveau composant VCL NumberBox (ajouté dans Delphi 10.4.2, voir par exemple https://blog.marcocantu.com/blog/2021-february -new-vcl-controls-1042.html ). Ce composant permet aux utilisateurs finaux d’entrer une expression et de la remplacer par la valeur résultante. Pas de surprise, il a utilisé le moteur d’expression existant et il le fait en appelant une méthode de classe simplifiée de
1 2 3 4 5 6 7 8 9 |
var LExpression: TBindingExpression; begin LExpression := TBindings.CreateExpression([], LText); try Value := LExpression.Evaluate.GetValue.AsExtended; finally LExpression.Free; end; |
Dans cet extrait de code, LText est une chaîne avec l’expression et Value est le résultat à virgule flottante. La méthode de classe TBindings.CreateExpression est un raccourci qui peut simplifier le code, mais je préfère vous guider vers plus de détails.
Table of Contents
Concepts clés des expressions de liaison
Le code ci-dessus crée un objet TBindingExpression, la classe principale de ce moteur. Comme son nom l’indique, cela va au-delà d’un simple évaluateur d’expression, mais peut « lier » ou associer l’expression à des objets externes, en utilisant RTTI pour l’intégration.
Un autre concept clé des expressions de liaison est qu’elles n’évaluent pas l’entrée de manière complètement dynamique, mais qu’elles nécessitent plutôt une opération d’analyse (appelée Compile) qui traite le texte et une opération d’évaluation qui effectue le traitement final. Bien sûr, si vous ne modifiez pas le texte de l’expression, vous pouvez compiler une fois et évaluer l’expression plusieurs fois avec des valeurs différentes pour les objets associés.
Passons à une démo
Pour ma première démo, j’ai créé un formulaire avec deux contrôles Memo, un pour taper l’expression et le second pour afficher la sortie. Le but ici est de traiter des chaînes, pas des valeurs numériques. Le code du seul bouton utilise directement les objets d’expression de liaison, comme suit :
1 2 3 4 5 6 7 8 9 10 |
procedure TForm1.btnEvalClick(Sender: TObject); var bindExpr: TBindingExpression; begin bindExpr := TBindingExpressionDefault.Create; bindExpr.Source := MemoExpr.Lines.Text; BindExpr.Compile(); MemoOut.Lines.Add (BindExpr.Evaluate.GetValue.ToString); bindExpr.Free; end; |
Ce n’est pas très utile, car la seule opération prédéfinie pour les chaînes est la concaténation, vous pouvez donc saisir l’entrée :
1 |
"Hello " + " world" |
et obtenez Hello world comme résultat. Remarquez les guillemets doubles dans l’entrée !
Lier un objet
Là où les choses commencent à devenir intéressantes, c’est si vous liez l’expression à un objet. Pour cela, j’ai créé une classe simple comme la suivante (ici j’ai omis les champs et méthodes privés) :
1 2 3 4 5 6 |
type TPerson = class public property Name: string read FName write SetName; property Age: Integer read FAge write SetAge; end; |
Maintenant, je peux changer le code pour ajouter une liaison à l’expression, en ajoutant à la méthode Compile l’association d’un objet avec un nom symbolique (vous pouvez le considérer comme le nom de l’objet pour le moteur d’expression) :
1 2 |
pers := TPerson.Create; BindExpr.Compile([TBindingAssociation.Create(pers, 'person')]); |
Avec cette extension, je peux maintenant utiliser cet objet dans une expression comme. Par exemple, après avoir affecté ‘John’ au Nom de l’ objet pers dans le code, l’expression :
1 |
person.name + " is happy" |
se traduirait par John est heureux . Mais je peux aussi utiliser l’expression :
1 |
person.name + " is " + person.age + " years old." |
qui va faire la transformation de type d’entier en chaîne.
Fonctions de liaison
Les expressions de liaison sont limitées aux objets, mais en plus d’accéder aux propriétés, elles peuvent également exécuter des méthodes. Par conséquent, vous pouvez créer une classe avec plusieurs méthodes à utiliser dans une expression
1 2 3 4 5 6 |
type TMyFunct = class public function Double (I: Integer): Integer; function ToStr (I: Integer): string; end; |
Pour invoquer ces méthodes, vous devez lier un objet de ce type :
1 2 3 |
BindExpr.Compile([ TBindingAssociation.Create(pers, 'person'), TBindingAssociation.Create(myFunct, 'fn')]); |
Vous pouvez maintenant écrire des expressions telles que :
1 |
"At double age " + person.name + " will be " + fn.ToStr(fn.Double(person.age)) + " years old" |
résultant en : À l’âge double, John aura 66 ans
L’interface utilisateur de démonstration
Pas si fantaisiste, mais voici la démo en action :
Ne toucher que la surface
Cette introduction ne fait qu’effleurer la surface, car l’expression de liaison permet des contrôles de liaison et permet également de s’inscrire aux notifications (une sorte de mécanisme de rappel). J’ai trouvé quelques autres démos que je vais essayer de bloguer à leur sujet bientôt.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition