Delphi 是否可以使用RTTI以编程方式更改属性写入方法以创建对象感知控件?

Delphi 是否可以使用RTTI以编程方式更改属性写入方法以创建对象感知控件?,delphi,delphi-2009,delphi-xe2,Delphi,Delphi 2009,Delphi Xe2,我有一个业务对象,我想更好地“连接”到我的UI。我已经看到了一些使对象具有数据意识的部分解决方案,但它们都涉及对我的业务对象的重大更改,包括额外的抽象层 我一直在研究Delphi新版本中改进的RTTI,它看起来非常有趣和有用。我想知道是否可以使用它以编程方式为所有属性注入新的写方法 这样做的方式是,在构建表单时,我的TEdit子代将通过引用对象属性来实现。然后,TEdit将在该属性的属性中插入对自身的引用(当然,在析构函数上删除自身,或者获得另一个引用)。TEdit还将确保将属性的写入方法替换为

我有一个业务对象,我想更好地“连接”到我的UI。我已经看到了一些使对象具有数据意识的部分解决方案,但它们都涉及对我的业务对象的重大更改,包括额外的抽象层

我一直在研究Delphi新版本中改进的RTTI,它看起来非常有趣和有用。我想知道是否可以使用它以编程方式为所有属性注入新的写方法

这样做的方式是,在构建表单时,我的TEdit子代将通过引用对象属性来实现。然后,TEdit将在该属性的属性中插入对自身的引用(当然,在析构函数上删除自身,或者获得另一个引用)。TEdit还将确保将属性的写入方法替换为在调用原始写入方法后通知TEdit更改的方法

这是否可行?最大的障碍是注入一个新的写方法是不可能的,因此这个问题的标题是


派生属性也存在潜在问题,但应该有可能找到解决方案。

您的问题已经让您在编程技能方面走在了我的前面,因此我只想补充一下如何解决这个问题:

如果我尝试写类似的东西,我可能会从TBusinessObject中每个字段的TList开始。该列表将用于指示当您需要推出更改时需要更新的内容

因此,当创建TEdit时,它会将自己添加到与您的TBusinessObject中的一段数据关联的列表中。当TBusinessObject更新该数据段时,它将在附加对象列表中运行。它会看到TEdit,知道它是TEdit,就会运行代码来更新.Text。如果我附加了一个t选项,那么代码将更新.Caption

正如您所指出的,TEdit需要在TBusinessObject的值更新时通知它。我想这是一个棘手的问题——你可以创建一个新的TEdit,并添加一个TList来维护它在发生变化时应该通知谁。如果您使用.Tag来指示TBusinessObject中的字段号,那么OnChange(或任何事件)可以调用类似TBusinessObject.FieldUpdate[TEdit.Tag,NewValue]的内容,然后触发您的业务逻辑。这反过来可能会使TBusinessObject更新其他字段,这些字段可能有自己的TList到要更新的字段

要防止循环更新,需要有一种在不触发事件的情况下更新控件的方法。对于我编写的一个程序,我有两种方法来更新控件:SetValue和ChangeValue。SetValue禁用任何事件(OnChange、OnValidate),更新控件的值,然后重新启用事件。ChangeValue只是更改了该值,并允许根据需要触发控件的任何事件

也许有更巧妙的方法可以做到这一点,但希望这能让你深思

是否可以使用RTTI以编程方式更改属性写入方法来创建对象感知控件

不,不可能。RTTI为您提供信息,但不提供在运行时更改类型的功能

最大的障碍是注入一个新的写方法是不可能的,因此这个问题的标题是

为了让您在运行时改变这一点,应该有类似于事件处理程序的东西,您可以设置它。这是一个简单的概念,但它有一些运行时开销,包括调用时间(直接调用通常就足够了)和所需内存(每个属性都需要额外的
TEvent
样式字段)。如果需要,这很容易实现,但如果编译器“以防万一”自动为所有类生成这样的代码,这将是有害的

如果您想在运行时以某种方式修补内存中的代码,那么这是行不通的,而且充其量也不可靠。

在这篇题为《诱导巨大鸿沟》的文章中,谈到了业务对象

他制作的解决方案基本上符合您的要求:

  • 利用Delphi最新版本中引入的高级RTTI功能
  • 业务逻辑和表示逻辑的分离
  • 任何PODO(普通的旧Delphi对象)都可以作为业务对象

    魔法在于TObjectBinding类,它将任何TWinControl与任何业务对象联系起来

    摘录:

    TObjectBinding = class
    private
      fCtx: TRttiContext;
      fControlType: TRttiType;
      fObjType: TRttiType;
    
      fPropFieldMapping: TDictionary<TRttiProperty, TRttiField>; // Dictionary of object Properties & corresponding Fields
    
      fControl: TWinControl; // The control (normally form)
      fObj: TObject; // Object it represents.
    
      procedure CreateMappings; 
    
      function FindField(Prop: TRttiProperty; out Field: TRttiField): Boolean;
      function FieldClass(Field: TRttiField): TClass;
    
      // Modify these to change the rules about what should be matched.
      function IsValidField(Field: TRttiField): Boolean;
      function IsValidProp(Prop: TRttiProperty): Boolean;
    
      // Modify these to change the mappings of property type to VCL control class.
      procedure AssignField(Prop: TRttiProperty; Field: TRttiField);
      procedure AssignProp(Prop: TRttiProperty; Field: TRttiField);
    
      // Used from AssignField/AssignProp. Extend these to support a wider range of properties.
      function GetPropText(Prop: TRttiProperty): string;
      procedure SetPropText(Prop: TRttiProperty; const Text: string);
    public
      constructor Create(Control: TWinControl; Obj: TObject);
      destructor Destroy; override;
      //
      procedure Load;
      procedure Save;
    end;
    
    TObjectBinding=class
    私有的
    fCtx:trtti上下文;
    fControlType:TRttiType;
    fObjType:trtti型;
    fPropFieldMapping:TDictionary;//对象属性和相应字段的字典
    fControl:TWinControl;//控件(通常为窗体)
    fObj:TObject;//它所代表的对象。
    程序映射;
    函数FindField(Prop:TRttiProperty;out字段:TRttiField):布尔值;
    功能字段类(字段:TRttiField):TClass;
    //修改这些以更改有关应匹配内容的规则。
    函数IsValidField(字段:TRttiField):布尔值;
    函数IsValidProp(Prop:TRttiProperty):布尔值;
    //修改这些以更改属性类型到VCL控制类的映射。
    过程赋值字段(属性:TRttiProperty;字段:TRttiField);
    过程赋值属性(属性:TRttiProperty;字段:TRttiField);
    //从AssignField/AssignProp使用。扩展这些功能以支持更广泛的属性。
    函数GetPropText(Prop:TRttiProperty):字符串;
    过程SetPropText(Prop:trtti属性;const Text:string);
    公众的
    构造函数创建(控件:TWinControl;对象:TObject);
    毁灭者毁灭;推翻
    //
    程序负载;
    程序保存;
    结束;
    
    我希望这将是一个很好的起点。

    也许是一个起点?听起来像宾迪