Delphi 从指向记录类型rtti字段的指针获取值

Delphi 从指向记录类型rtti字段的指针获取值,delphi,delphi-xe2,rtti,delphi-xe3,Delphi,Delphi Xe2,Rtti,Delphi Xe3,我在使用Delphi的RTTI访问记录数据中记录类型的指针时遇到了麻烦 请检查我一直在处理的示例代码 // Dummy Header typDummyHeader = ^tysDummyHeader; tysDummyHeader = record MessageCode : Integer; MessageLength : Integer; end; // Dummy record having header and trailer tysDummyR

我在使用Delphi的RTTI访问记录数据中记录类型的指针时遇到了麻烦

请检查我一直在处理的示例代码

  // Dummy Header
  typDummyHeader = ^tysDummyHeader;
  tysDummyHeader = record
    MessageCode : Integer;
    MessageLength : Integer;
  end;

  // Dummy record having header and trailer
  tysDummyRecord = record
    Header : tysDummyHeader;
    BotAmount : Double;
    SoldAmount : Double;
    SoldQty : Int64;
    BotQty : Int64;
    Tailer : typDummyHeader; // pointer to Dummy Header
  end;

  TclDummy = class
    class function GetFieldValue<T>(const pipInstance : Pointer;
                                const piclField : TRttiField) : string;

    class function ParseAndReturnString<T>(piclObject : T) : string;
  end;

  var
    frmRTTITest: TfrmRTTITest;

  implementation

  {$R *.dfm}

  procedure TfrmRTTITest.FormCreate(Sender: TObject);
  var
    losDummyRecord : tysDummyRecord;
  begin
    FillChar(losDummyRecord, SizeOf(tysDummyRecord), #0);
    losDummyRecord.Header.MessageCode := 5000;
    losDummyRecord.Header.MessageLength := 54433;
    losDummyRecord.BotAmount := 19.45;
    losDummyRecord.SoldAmount := 34.22;
    losDummyRecord.SoldQty := 102;
    losDummyRecord.BotQty := 334;
    losDummyRecord.Tailer := @losDummyRecord.Header;

    ShowMessage(TclDummy.ParseAndReturnString<tysDummyRecord>(losDummyRecord));
  end;

  class function TclDummy.GetFieldValue<T>(const pipInstance : Pointer;
                               const piclField : TRttiField) : string;
  begin
    case piclField.FieldType.TypeKind of
      tkFloat: Result := FloatToStr(piclField.GetValue(pipInstance).AsExtended);
      tkInt64: Result := IntToStr(piclField.GetValue(pipInstance).AsInt64);
      tkInteger: Result := IntToStr(piclField.GetValue(pipInstance).AsInteger);
      tkString: Result := Trim(piclField.GetValue(pipInstance).AsString);
    end;
  end;

  class function TclDummy.ParseAndReturnString<T>(piclObject : T) : string;
  var
    losContext : TRttiContext;
    losContextType : TRttiType;
    loclField : TRttiField;
    losRecordRTTI : TRttiRecordType;
    loclRecordField : TRttiField;
    losPointerType : TRttiPointerType;
    losValue : TValue;
  begin
    Result := EmptyStr;
    losContext := TRttiContext.Create;
    losContextType := losContext.GetType(TypeInfo(T));

    if losContextType.TypeKind = tkRecord then
    begin
      for loclField in losContextType.GetFields do
      begin
        case loclField.FieldType.TypeKind of
          tkRecord:
          begin
            losRecordRTTI := loclField.FieldType.AsRecord;
            for loclRecordField in losRecordRTTI.GetFields do
            begin
              Result := Result + '|' + GetFieldValue<T>(Addr(piclObject), loclRecordField);
            end;
          end; // tkRecord
          tkPointer:
          begin
            losPointerType := loclField.FieldType as TRttiPointerType;

            // Check only record type pointers.
            if losPointerType.ReferredType.TypeKind = tkRecord then
            begin
              losValue := loclField.GetValue(Addr(piclObject));
              if (not losValue.IsEmpty) then
              begin
                for loclRecordField in losPointerType.ReferredType.GetFields do
                begin
                  // Result := Result + '|' + ???????????
                end;
              end;
            end;
          end; // tkPointer
          else
            Result := Result + '|' + GetFieldValue<T>(Addr(piclObject), loclField);
        end;
      end;
    end;
    losContext.Free;
  end;
//伪头
typDummyHeader=^tysDummyHeader;
tysDummyHeader=记录
MessageCode:整数;
MessageLength:整数;
结束;
//具有头和尾的虚拟记录
tysDummyRecord=记录
标题:tysDummyHeader;
金额:双倍;
索尔达蒙特:双倍;
SoldQty:Int64;
数量:Int64;
Tailer:typDummyHeader;//指向伪标题的指针
结束;
TclDummy=class
类函数GetFieldValue(const pipInstance:指针;
常量piclField:TRttiField):字符串;
类函数ParseAndReturnString(piclObject:T):string;
结束;
变量
frmRTTITest:TFRMRTITEST;
实施
{$R*.dfm}
过程TfrmRTTITest.FormCreate(发送方:TObject);
变量
losDummyRecord:tysDummyRecord;
开始
FillChar(losDummyRecord,SizeOf(tysDummyRecord),0);
losDummyRecord.Header.MessageCode:=5000;
losDummyRecord.Header.MessageLength:=54433;
losDummyRecord.BotAmount:=19.45;
losDummyRecord.SoldAmount:=34.22;
losDummyRecord.SoldQty:=102;
losDummyRecord.BotQty:=334;
losDummyRecord.Tailer:=@losDummyRecord.Header;
ShowMessage(tclsdummy.ParseAndReturnString(losdumyRecord));
结束;
类函数tclsdummy.GetFieldValue(const pipInstance:Pointer;
常量piclField:TRttiField):字符串;
开始
case piclField.FieldType.TypeKind of
tkFloat:Result:=FloatToStr(piclField.GetValue(pipInstance.AsExtended);
tkInt64:Result:=IntToStr(piclField.GetValue(pipInstance.AsInt64);
tkInteger:Result:=IntToStr(piclField.GetValue(pipInstance.AsInteger));
tkString:Result:=Trim(piclField.GetValue(pipInstance.AsString);
结束;
结束;
类函数tclmdummy.ParseAndReturnString(piclObject:T):string;
变量
losContext:TRttiContext;
losContextType:TrtType;
loclField:TRttiField;
losRecordRTTI:TRttiRecordType;
loclRecordField:TRttiField;
losPointerType:TrtPointerType;
losValue:TValue;
开始
结果:=EmptyStr;
losContext:=TRttiContext.Create;
losContextType:=losContext.GetType(TypeInfo(T));
如果losContextType.TypeKind=tkRecord,则
开始
对于losContextType.GetFields中的loclField
开始
case loclField.FieldType.TypeKind of
tkRecord:
开始
losRecordRTTI:=loclField.FieldType.AsRecord;
对于losRecordRTTI.GetFields中的loclRecordField
开始
结果:=Result+'|'+GetFieldValue(Addr(piclObject),loclRecordField);
结束;
完tkRecord
tkPointer:
开始
losPointerType:=loclField.FieldType作为TrtPointerType;
//只检查记录类型指针。
如果losPointerType.ReferredType.TypeKind=tkRecord,则
开始
losValue:=loclField.GetValue(Addr(piclObject));
如果(不是losValue.IsEmpty),那么
开始
对于losPointerType.ReferredType.GetFields中的loclRecordField
开始
//结果:=结果+'|'+???????????
结束;
结束;
结束;
完TK指针
其他的
结果:=Result+'|'+GetFieldValue(Addr(piclObject),loclField);
结束;
结束;
结束;
losscontext.Free;
结束;
在上面的示例中,当字段是指向记录类型的
tkPointer
时,如何从中读取值?

Result:=Result+'|'+GetFieldValue(Addr(piclObject),loclRecordField);
Result := Result + '|' + GetFieldValue<T>(Addr(piclObject),loclRecordField);

我应该做这项工作。

对不起,我的英语很差

@bummi的答案有效,但不正确

这取决于使用情况

如果使用下一个代码,则所有代码都可以正常工作:

var
  losDummyRecord : tysDummyRecord;
begin
  FillChar(losDummyRecord, SizeOf(tysDummyRecord), #0);
  losDummyRecord.Header.MessageCode := 5000;
  losDummyRecord.Header.MessageLength := 54433;
  losDummyRecord.BotAmount := 19.45;
  losDummyRecord.SoldAmount := 34.22;
  losDummyRecord.SoldQty := 102;
  losDummyRecord.BotQty := 334;
  losDummyRecord.Tailer := @losDummyRecord.Header;

  ShowMessage(TclDummy.ParseAndReturnString<tysDummyRecord>(losDummyRecord));
var
losDummyRecord:tysDummyRecord;
开始
FillChar(losDummyRecord,SizeOf(tysDummyRecord),0);
losDummyRecord.Header.MessageCode:=5000;
losDummyRecord.Header.MessageLength:=54433;
losDummyRecord.BotAmount:=19.45;
losDummyRecord.SoldAmount:=34.22;
losDummyRecord.SoldQty:=102;
losDummyRecord.BotQty:=334;
losDummyRecord.Tailer:=@losDummyRecord.Header;
ShowMessage(tclsdummy.ParseAndReturnString(losdumyRecord));
但如果您使用此代码,解析将无法正常工作:

var
  losDummyRecord : tysDummyRecord;
  ExternalHeaderVar: tysDummyHeader;
begin
  ExternalHeaderVar.MessageCode := 23;
  ExternalHeaderVar.MessageLength := 25;

  FillChar(losDummyRecord, SizeOf(tysDummyRecord), #0);
  losDummyRecord.Header.MessageCode := 5000;
  losDummyRecord.Header.MessageLength := 54433;
  losDummyRecord.BotAmount := 19.45;
  losDummyRecord.SoldAmount := 34.22;
  losDummyRecord.SoldQty := 102;
  losDummyRecord.BotQty := 334;
  losDummyRecord.Tailer := @ExternalHeaderVar;

  ShowMessage(TclDummy.ParseAndReturnString<tysDummyRecord>(losDummyRecord));
var
losDummyRecord:tysDummyRecord;
外部头服务器:tysDummyHeader;
开始
ExternalHeaderVar.MessageCode:=23;
ExternalHeaderVar.MessageLength:=25;
FillChar(losDummyRecord,SizeOf(tysDummyRecord),0);
losDummyRecord.Header.MessageCode:=5000;
losDummyRecord.Header.MessageLength:=54433;
losDummyRecord.BotAmount:=19.45;
losDummyRecord.SoldAmount:=34.22;
losDummyRecord.SoldQty:=102;
losDummyRecord.BotQty:=334;
losDummyRecord.Tailer:=@ExternalHeaderVar;
ShowMessage(tclsdummy.ParseAndReturnString(losdumyRecord));
解决方案简单,在这两种情况下都能很好地工作,并可防止将来使用时出现意外错误:

      tkPointer:
      begin
        losPointerType := loclField.FieldType as TRttiPointerType;

        // Check only record type pointers.
        if losPointerType.ReferredType.TypeKind = tkRecord then
        begin
          losValue := loclField.GetValue(Addr(piclObject));
          if (not losValue.IsEmpty) then
          begin
            losValue.ExtractRawDataNoCopy(@NativeIntVar);
            for loclRecordField in losPointerType.ReferredType.GetFields do
            begin
              Result := Result + '|' + GetFieldValue<T>(Pointer(NativeIntVar),loclRecordField);
            end;
          end;
        end;
      end; // tkPointer
tkPointer:
开始
losPointerType:=loclField.FieldType作为TrtPointerType;
//只检查记录类型指针。
如果losPointerType.ReferredType.TypeKind=tkRecord,则
开始
losValue:=loclField.GetValue(Addr(piclObject));
如果(不是losValue.IsEmpty),那么
开始
losValue.ExtractRawDataNoCopy(@NativeIntVar);
对于losPointerType.ReferredType.GetFields中的loclRecordField
开始
结果:=Result+'|'+GetFieldValue(指针(NativeIntVar),LocalRecordField);
结束;
结束;
结束;
完TK指针

基本上,我想做的是得到一个字符串co