如何使用Delphi RTTI获取和设置记录值

Mit*_*tch 11 delphi rtti delphi-xe

我试图在Delphi XE或更高版本中使用增强的RTTI功能来读取和写入XML对象.到目前为止,我已成功使用整数,浮点数,字符串,枚举类型,集合和类,但无法正确输出或读取记录.问题似乎是获取记录属性的实例(指针).

//Outputs Properties To XML
procedure TMyBase.SaveToXML(node: TJclSimpleXMLElem);
var
  child , subchild : TjclSimpleXMLElem ;
  FContext : TRttiContext ;
  FType    : TRttiType ;
  FProp    : TRttiProperty ;
  Value    : TValue ;
  MyObj    : TMyBase ;
  FField   : TRttiField ;
  FRecord  : TRttiRecordType ;
  Data     : TValue ;
begin
  FContext := TRttiContext.Create ;
  FType := FContext.GetType ( self.ClassType ) ;
  Child := node.Items.Add ( ClassName ) ;
  for FProp in FType.GetProperties do begin
    if FProp.IsWritable then begin
      case FProp.PropertyType.TypeKind of
        tkClass : begin
          MyObj := TMyBase ( FProp.GetValue ( self ).AsObject ) ;
          MyObj.SaveClass ( Child.Items.Add ( FProp.Name ) , FContext ) ;
          end ;
        tkRecord : begin
          subchild := Child.Items.Add ( FProp.Name ) ;
          FRecord := FContext.GetType(FProp.GetValue(self).TypeInfo).AsRecord ;
          for FField in FRecord.GetFields do begin
            >>> self is not the correct instance <<<
            Value := FField.GetValue ( self ) ;
            subchild.Items.Add ( FField.Name ).Value := Value.ToString ;
            end;
          end ;
        else begin
          Value := FProp.GetValue(self) ;
          Child.Items.Add ( FProp.Name ).Value := Value.ToString ;
          end;
        end;
      end ;
    end ;
  FContext.Free ;
end;
Run Code Online (Sandbox Code Playgroud)

我怀疑,如果我能弄清楚如何获取值,那么设置它们应该不是问题.然后到阵列上,哦,男孩!

更新:请参阅下文.(作为单独的答案迁移以提高可见性).

Bar*_*lly 12

我假设您正在尝试保存Self的运行时类型的记录类型字段的值,是吗?

您必须首先获得该字段的值FProp.GetValue(Self).假设你把它放在一个名为FieldValuetype 的变量中TValue.然后,您可以根据需要保存记录值的字段,但您可能希望为其编写递归过程,因为记录的字段本身可能是字段.记录的字段getter需要记录的地址(指向其开头的指针)以便与setter对称; setter期望地址而不是值,因为否则就没有简单的方法来修改另一个类或记录中的"in situ"字段,因为记录会以值传递.

您可以使用FieldValue.GetReferenceToRawData,它将返回指向存储在其中的记录的开头的指针TValue.

希望这能为你提供足够的线索来继续.

  • 你们中的任何一个老同学都记得Turbo pascal的日子,当你通过创建"F:MyRecord文件"来保留记录,分配(F,文件名),然后将二进制记录内容写入文件? (2认同)

men*_*raz 5

出处:最初由OP(Mitch )发布为问题的更新-迁移为单独的答案以提高可见性。

巴里的解决方案成功了。这是修改后的代码:

    tkRecord : begin
      subchild := Child.Items.Add ( FProp.Name ) ;
      Value := FProp.GetValue(self) ;
      FRecord := FContext.GetType(FProp.GetValue(self).TypeInfo).AsRecord ;
      for FField in FRecord.GetFields do begin
        Data := FField.GetValue ( Value.GetReferenceToRawData ) ;
        subchild.Items.Add ( FField.Name ).Value := Data.ToString ;
        end;
      end ;
Run Code Online (Sandbox Code Playgroud)

对于那些需要处理数组的对象:

    tkDynArray : begin
      Value := FProp.GetValue ( self ) ;
      FArray := FContext.GetType(Value.TypeInfo) as TRttiDynamicArrayType ;
      subchild := child.Items.Add ( FProp.Name ) ;
      cnt := Value.GetArrayLength ;
      subchild.Properties.Add ( 'Count' , cnt ) ;
      case FArray.ElementType.TypeKind of
        tkInteger ,
        tkFloat   : begin
          for a := 0 to cnt-1 do begin
            Data := Value.GetArrayElement ( a ) ;
            subchild.Items.Add ( IntToStr(a) , Data.ToString ) ;
            end;
          end ;
        tkRecord  : begin
          FRecord := FArray.ElementType as TRttiRecordType ;
          for a := 0 to cnt-1 do begin
            Data := Value.GetArrayElement ( a ) ;
            subsubchild := subchild.Items.Add ( IntToStr(a) ) ;
            for FField in FRecord.GetFields do
              SaveField ( subsubchild , FContext , FField , Data.GetReferenceToRawData ) ;
            end;
          end ;
Run Code Online (Sandbox Code Playgroud)