Delphi RTL inclui um motor de expressão muito poderoso, que é uma das bases da arquitetura Live Bindings, mas pode ser usado como um motor separado para o processamento de expressões. Esta postagem do blog oferece uma introdução rápida ao tópico.
Existem muitas joias escondidas no Delphi RTL. Um deles é o motor de expressão. Recentemente, tive algumas conversas com um desenvolvedor Delphi de longa data que estava procurando por um recurso semelhante, sem perceber que já estava no produto há muitos anos. Eu sabia que tinha escrito algumas demos e documentação, então gastei um pouco de tempo procurando por elas e encontrei algumas. Agora, este tópico é muito complexo e não serei capaz de abordá-lo por completo, mas para cenários simples, você realmente precisa de muito pouco código para analisar e processar uma expressão.
Antes de começar com o tópico real, também gostaria de mencionar que recentemente destacamos esse recurso no novo componente VCL NumberBox (adicionado no Delphi 10.4.2, consulte por exemplo https://blog.marcocantu.com/blog/2021-february -new-vcl-controls-1042.html ). Este componente permite aos usuários finais inserir uma expressão e substituí-la pelo valor resultante. Sem surpresa, ele usou o mecanismo de expressão existente e o faz invocando um método de classe simplificado 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; |
Neste trecho de código, LText é uma string com a expressão e Value é o resultado do ponto flutuante. O método de classe TBindings.CreateExpression é um atalho que pode simplificar o código, mas prefiro guiá-lo para mais alguns detalhes.
Table of Contents
Principais conceitos de expressões de ligação
O código acima cria um objeto TBindingExpression, a classe principal deste mecanismo. Como o nome indica, ele vai além de ser um avaliador de expressão pura, mas pode “vincular” ou associar a expressão a objetos externos, usando RTTI para integração.
Outro conceito chave das expressões de ligação é que elas não avaliam a entrada de uma maneira completamente dinâmica, mas requerem uma operação de análise (chamada Compilar) que processa o texto e uma operação de avaliação que faz o processamento final. Obviamente, se você não alterar o texto da expressão, poderá compilar uma vez e avaliar a expressão várias vezes com valores diferentes para os objetos associados.
Vamos fazer uma demonstração
Para minha primeira demonstração, criei um formulário com dois controles Memo, um para digitar a expressão e o segundo para exibir a saída. O objetivo aqui é processar strings, não valores numéricos. O código para o único botão usa os objetos de expressão de ligação diretamente, da seguinte maneira:
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; |
Não que seja muito útil, já que a única operação predefinida para strings é a concatenação, então você pode digitar a entrada:
1 |
"Hello " + " world" |
e obter Hello world como resultado. Observe as aspas duplas na entrada!
Vinculando um objeto
As coisas começam a ficar interessantes quando você associa a expressão a um objeto. Para isso, criei uma classe simples como a seguinte (omiti aqui os campos e métodos privados):
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; |
Agora posso alterar o código para adicionar um vínculo à expressão, adicionando ao método Compile a associação de um objeto com um nome simbólico (você pode considerá-lo como o nome do objeto para o mecanismo de expressão):
1 2 |
pers := TPerson.Create; BindExpr.Compile([TBindingAssociation.Create(pers, 'person')]); |
Com essa extensão, agora posso usar esse objeto em uma expressão como. Por exemplo, depois de atribuir ‘John’ ao nome do objeto pessoa no código, a expressão:
1 |
person.name + " is happy" |
resultaria em John está feliz . Mas também posso usar a expressão:
1 |
person.name + " is " + person.age + " years old." |
que fará a transformação do tipo de inteiro em string.
Funções de ligação
As expressões de ligação são limitadas a objetos, mas além de acessar as propriedades, elas também podem executar métodos. Portanto, você pode criar uma classe com vários métodos para serem usados em uma expressão:
1 2 3 4 5 6 |
type TMyFunct = class public function Double (I: Integer): Integer; function ToStr (I: Integer): string; end; |
Para invocar esses métodos, você precisa vincular um objeto deste tipo:
1 2 3 |
BindExpr.Compile([ TBindingAssociation.Create(pers, 'person'), TBindingAssociation.Create(myFunct, 'fn')]); |
Agora você pode escrever expressões como:
1 |
"At double age " + person.name + " will be " + fn.ToStr(fn.Double(person.age)) + " years old" |
resultando em: Com a idade dupla, John terá 66 anos
A IU de demonstração
Não muito sofisticado, mas esta é a demonstração em ação:
Apenas tocando a superfície
Esta introdução está apenas tocando a superfície, já que a expressão de ligação permite controles de ligação e também permite o registro de notificações (uma espécie de mecanismo de retorno de chamada). Eu encontrei mais algumas demos que tentarei postar no blog sobre elas em breve.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition