原文地址: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"
結果:紅魚兒的年紀增大了一倍。
演示界面
不是那么花哨,但這是正在運行的演示:
總結
這里只是簡單的入門介紹,因為還有象綁定表達式並允許綁定控件及允許注冊通知(一種回調機制)等需求,我將找更多的演示,並將盡快嘗試在博客中介紹它們。