2013-02-20 104 views
2

我遇到了麻煩,試圖使用Delphi的RTTI訪問我的記錄數據中的記錄類型指針。從指針獲取值到記錄類型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; 

在上述樣品中,當字段是指向一個記錄類型tkPointer,如何讀取這些值?

+0

基本上我想要做的是得到字符串與記錄中的所有成員連接,即使它包含嵌套記錄。 – 2013-02-20 12:36:43

+0

這不就是ParseAndReturnString已經做了什麼嗎?以遞歸方式調用它。 – 2013-02-20 13:51:23

+0

@DavidHeffernan我需要從指針類型中獲取值。請檢查評論與????????????? – 2013-02-20 14:04:39

回答

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

應該做的工作。

1

對不起,我英文很差。

@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; 
    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)); 

解決方案很簡單,在兩個工作案件很好,並防止在未來使用意外的錯誤:

 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