【译】应用Delphi 表达式引擎
原文地址: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)和一个进行最终处理的评估操作。当然,如果不更改表达式文本,则可以编译一次并用关联对象的不同值多次评估表达式。
Demo
对于第一个Demo,我创建了带有两个Memo控件的Form,第一个Memo用来输入表达式,第二个Memo显示结果。这里的目标是处理字符串,而不是数字值,Button的代码直接使用绑定表达式对象,如下所示:
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的类型转换。
绑定函数
绑定表达式仅限于对象,但除了访问属性之外,它们还可以执行方法。因此,您可以创建一个类,其中包含要在表达式中使用的多种方法:
type TMyFunct = class public function Double (I: Integer): Integer; function ToStr (I: Integer): string; end;
要调用这些方法,您需要绑定一个这种类型的对象:
BindExpr.Compile([ TBindingAssociation.Create(pers, 'person'), TBindingAssociation.Create(myFunct, 'fn')]);
现在您可以编写如下表达式:
"At double age " + person.name + " will be " + fn.ToStr(fn.Double(person.age)) + " years old"
结果:红鱼儿的年纪增大了一倍。
演示界面
不是那么花哨,但这是正在运行的演示:
总结
这里只是简单的入门介绍,因为还有象绑定表达式并允许绑定控件及允许注册通知(一种回调机制)等需求,我将找更多的演示,并将尽快尝试在博客中介绍它们。