![bindingexpressions | Embarcadero RAD Studio Delphi C++Builder Blogs bindingexpressions](https://i0.wp.com/blogs.embarcadero.com/wp-content/uploads/2021/05/bindingexpressions-9776747.png?resize=1140%2C694&ssl=1)
Delphi RTL включает очень мощный механизм выражений, который является одной из основ архитектуры Live Bindings, но может использоваться как отдельный механизм для обработки выражений. Это сообщение в блоге предлагает краткое введение в тему.
В Delphi RTL есть много скрытых жемчужин. Один из них — машина выражения. Недавно у меня было несколько разговоров с давним разработчиком Delphi, который искал похожую функцию, не понимая, что она присутствует в продукте много лет. Я знал, что написал несколько демонстраций и документации, поэтому потратил немного времени на их поиск и нашел несколько. Эта тема очень сложна, и я не смогу охватить ее полностью, но для простых сценариев вам действительно понадобится очень мало кода для синтаксического анализа и обработки выражения.
Прежде чем я начну с реальной темы, я также хотел бы упомянуть, что мы недавно обнаружили эту функцию в новом компоненте VCL NumberBox (добавленном в Delphi 10.4.2, см., Например, https://blog.marcocantu.com/blog/2021-february—new-vcl-controls-1042.html ). Этот компонент позволяет конечным пользователям вводить выражение и заменять его полученным значением. Неудивительно, что он использовал существующий механизм выражений и делает это, вызывая упрощенный метод класса
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; |
В этом фрагменте кода LText — это строка с выражением, а Value — результат с плавающей запятой. Метод класса TBindings.CreateExpression — это ярлык, который может упростить код, но я бы предпочел познакомить вас с некоторыми подробностями.
Table of Contents
Ключевые концепции связывающих выражений
Приведенный выше код создает объект TBindingExpression, основной класс этого движка. Как следует из названия, он выходит за рамки простого оценщика выражений, но может «связывать» или связывать выражение с внешними объектами, используя RTTI для интеграции.
Еще одна ключевая концепция выражений привязки заключается в том, что они не оценивают ввод полностью динамическим образом, а скорее требуют операции синтаксического анализа (называемой компиляцией), обрабатывающей текст, и операции оценки, выполняющей окончательную обработку. Конечно, если вы не меняете текст выражения, вы можете один раз скомпилировать и многократно оценить выражение с разными значениями для связанных объектов.
Давайте перейдем к демонстрации
Для моей первой демонстрации я создал форму с двумя элементами управления Memo, один для ввода выражения, а второй для отображения вывода. Цель здесь — обрабатывать строки, а не числовые значения. Код для единственной кнопки напрямую использует объекты выражения привязки, как показано ниже:
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; |
Не то чтобы это было очень полезно, поскольку единственные предопределенные операции для строк — это конкатенация, поэтому вы можете ввести ввод:
1 |
"Hello " + " world" |
и в результате получите Hello world . Обратите внимание на двойные кавычки во вводе!
Связывание объекта
Все становится интересно, если вы привязываете выражение к объекту. Для этого я создал простой класс, подобный следующему (здесь я пропустил частные поля и методы):
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; |
Теперь я могу изменить код, чтобы добавить привязку к выражению, добавив к методу Compile ассоциацию объекта с символическим именем (вы можете рассматривать его как имя объекта для механизма выражения):
1 2 |
pers := TPerson.Create; BindExpr.Compile([TBindingAssociation.Create(pers, 'person')]); |
С этим расширением я теперь могу использовать этот объект в выражении вроде. Например, после присвоения «John» имени объекта pers в коде выражение:
1 |
person.name + " is happy" |
приведет к тому, что Джон счастлив . Но я также могу использовать выражение:
1 |
person.name + " is " + person.age + " years old." |
который будет выполнять преобразование типа из Integer в строку.
Функции привязки
Выражения привязки ограничены объектами, но помимо доступа к свойствам они также могут выполнять методы. Поэтому вы можете создать класс с несколькими методами, которые будут использоваться в выражении:
1 2 3 4 5 6 |
type TMyFunct = class public function Double (I: Integer): Integer; function ToStr (I: Integer): string; end; |
Чтобы вызвать эти методы, вам нужно привязать объект этого типа:
1 2 3 |
BindExpr.Compile([ TBindingAssociation.Create(pers, 'person'), TBindingAssociation.Create(myFunct, 'fn')]); |
Теперь вы можете писать такие выражения, как:
1 |
"At double age " + person.name + " will be " + fn.ToStr(fn.Double(person.age)) + " years old" |
Результат: В двойном возрасте Джону будет 66 лет.
Демо-интерфейс
Не то чтобы фантастика, но это демонстрация в действии:
Только касание поверхности
Это введение касается только поверхности, поскольку выражение привязки позволяет связывать элементы управления, а также позволяет регистрироваться для уведомлений (своего рода механизм обратного вызова). Я нашел еще несколько демонстраций, и скоро постараюсь опубликовать о них в блоге.
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition