https://blogs.embarcadero.com/using-delphis-expressions-engine/
Delphi RTL包含一个非常强大的表达式引擎,它虽然是Live Bindings体系结构的基础之一,但是可以用来处理表达式的单独引擎。这篇文章将对该主题做快速的入门。
Delphi RTL中有许多隐藏的宝贝。表达式引擎就是其中之一。最近,我与一位老Delphi开发人员交流过程中,发现他正在寻找类似的功能,这说明他没意识到该功能已存在于RTL中很多年了。记得我写过一些文档,所以用点时间查找它们,还真找到了。面对这个主题,实际上是非常复杂的,我无法完整介绍它,但是对于简单的场景,实际上只需要很少的代码就能解析和处理表达式。
在我们开始讨论这个主题之前,还是提一下Delphi 10.4.2新的VCL NumberBox组件填加的功能(参见 https://blog.marcocantu.com/blog/2021-february -new-vcl-controls-1042.html)。该组件允许最终用户输入表达式并将其替换为结果值。毫不奇怪,它使用了现有的表达式引擎,并且通过调用简化的class方法来做到这一点。
var LExpression: TBindingExpression; begin LExpression := TBindings.CreateExpression([], LText); try Value := LExpression.Evaluate.GetValue.AsExtended; finally LExpression.Free; end;
在此代码段中, LText 是带有表达式的字符串,而 Value 是浮点结果。TBindings.CreateExpression类方法是可以简化代码的快捷方式,但我希望为您提供更多详细信息。
绑定表达式的关键概念
上面的代码创建一个TBindingExpression对象,该对象是此引擎的核心类。顾名思义,这不仅仅是一个纯粹的表达式计算器,而且可以使用RTTI进行集成,从而将表达式“绑定”或关联到外部对象。
绑定表达式的另一个关键概念是它们并不以完全动态的方式评估输入,而是需要一个处理文本的解析操作(称为Compile)和一个进行最终处理的评估操作。当然,如果不更改表达式文本,则可以编译一次并用关联对象的不同值多次评估表达式。
让我们去演示
对于我的第一个演示,我创建了带有两个Memo控件的表单,一个控件用于键入表达式,第二个控件用于显示输出。这里的目标是处理字符串,而不是数字值。唯一按钮的代码直接使用绑定表达式对象,如下所示:
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;
这并不是很有用,因为字符串的唯一预定义操作是连接,因此您可以输入以下内容:
"Hello " + " world"
并获得 Hello world的结果。注意输入中的双引号!
绑定对象
如果您将表达式绑定到对象,那么事情就会开始变得有趣起来。为此,我创建了一个简单的类,如下所示(这里我省略了私有字段和方法):
type TPerson = class public property Name: string read FName write SetName; property Age: Integer read FAge write SetAge; end;
现在,我可以更改代码以添加到表达式的绑定,将对象与符号名称的关联添加到Compile方法(您可以将其视为表达式引擎的对象名称):
pers := TPerson.Create; BindExpr.Compile([TBindingAssociation.Create(pers, ‘person‘)]);
有了这个扩展,我现在可以在类似表达式中使用该对象。例如,在代码中为pers对象的名称分配“ John”后,表达式为:
person.name + " is happy"
会产生红鱼儿快乐。但我也可以使用以下表达式:
person.name + " is " + person.age + " years old."
这将执行从Integer到String的类型转换。