【问题标题】:access all elements of a record using RTTI使用 RTTI 访问记录的所有元素
【发布时间】:2014-05-23 07:47:37
【问题描述】:

我想将复杂/长记录转储到备忘录中以进行调试

 TmyRecord =
     aValue : String 
     aNumber : Real;
     Morenumbers   :  Integer ;
     ....
     ....
  end;

我认为 Delphi XE 2 RTTI 应该让我有机会获得 Fieldname 、 Fieldtype 和 value 在循环中,将此记录写入备忘录或.....

【问题讨论】:

  • 你需要多复杂?记录中只有简单类型?嵌套记录?变体记录?任意性质的复杂类型?您对 RTTI 有任何经验吗?没有证据表明你尝试过任何事情。网上有很多例子。您可能比使用众多 JSON 库之一将其转储到 JSON 做得更糟。这肯定会引导您获得许多优秀的示例代码。
  • procedure EnumerateFieldandValues(const AObject: TObject; RecordParams: TStringList);变量 i:整数; rtype:TRTTIType;字段:TArray;开始 rtype := TRttiContext.Create.GetType(TypeInfo(aObject.ClassType)); // Memo1.Lines.Add(rtype.ToString);字段:= rtype.GetFields; for i := 0 to High(fields) 做 RecordParams.Add(Format('%s: %s :: %s', [fields[i].Name, fields[i].FieldType.ToString, fields[i] .GetValue(@m).ToString]));结束;

标签: delphi rtti


【解决方案1】:

作为起点 - 使用简单类型记录。对于复杂字段(数组、类等),请探索 RTTI 单元

type
  TmyRecord = record
    aValue: String;
    aNumber: Real;
    Morenumbers: Integer;
  end;
var
  m: TMyRecord;
  rtype: TRTTIType;
  fields: TArray<TRttiField>;
  i: Integer;
begin
  m.aValue := 'OK';
  m.aNumber := Pi;
  m.Morenumbers := 666;
  rtype := TRTTIContext.Create.GetType(TypeInfo(TMyrecord));
  Memo1.Lines.Add(rtype.ToString);
  fields := rtype.GetFields;
  for i := 0 to High(fields) do
    Memo1.Lines.Add(Format('%s: %s :: %s', [
      fields[i].Name,
      fields[i].FieldType.ToString,
      fields[i].GetValue(@m).ToString]));

输出:

TmyRecord
aValue: string :: OK
aNumber: Real :: 3.14159265358979
Morenumbers: Integer :: 666

【讨论】:

    【解决方案2】:

    这是我的尝试。我和你有类似的任务(参考this thread)。它正在进行中,但到目前为止工作做得足够好。这将枚举TObject thou 中的所有属性,因此您必须对其进行调整以枚举记录:

    function EnumerateProperties(const AObject: TObject): String;
    var
      rt: TRttiType;
      prop: TRttiProperty;
      value, value2: TValue;
      valstr: String;
      propstr: String;
      fullstr: String;
      bres: Boolean;
      meth: TRttiMethod;
      bytes: TBytes;
      bytes_arr: TArray<TBytes>;
      uints: TArray<UINT32>;
      C1: Integer;
    begin
      if not Assigned(AObject) then
        Exit('');
    
      rt := TRttiContext.Create.GetType(AObject.ClassType);
    
      fullstr := '';
      // iterate through public properties
      for prop in rt.GetDeclaredProperties do
      begin
        value := prop.GetValue(AObject); // get property value
        valstr := '?';
    
        // check property type
        case prop.PropertyType.TypeKind of
          tkInteger,
          tkInt64,
          tkFloat: valstr := value.AsVariant;
    
          tkString,
          tkChar,
          tkWChar,
          tkLString,
          tkWString,
          tkUString: valstr := QuotedStr(value.AsString);
    
          tkEnumeration: begin
            valstr := 'ENUM';
            if value.TryAsType<Boolean>(bres) then
              valstr := BoolToStr(bres, TRUE)
            else
            begin
              valstr := GetEnumName(value.TypeInfo, prop.GetValue(AObject).AsOrdinal);
            end;
          end;
    
          tkClass: begin
            // check if property is TList or any of its descendants,
            // then iterate through each of it's members
            meth := prop.PropertyType.GetMethod('ToArray');
            if Assigned(meth) then
            begin
              value2 := meth.Invoke(value, []);
              Assert(value2.IsArray);
              for C1 := 0 to value2.GetArrayLength - 1 do
                valstr := valstr + Format('(%s), ', [EnumerateProperties(value2.GetArrayElement(C1).AsObject)]);
              if valstr <> '' then
                Delete(valstr, Length(valstr) - 1, 2);
              valstr := Format('[%s]', [valstr]);
            end
            else // otherwise, process it as normal class
              valstr := Format('[%s]', [EnumerateProperties(value.AsObject)]);
          end;
    
          // dynamic arrays
          tkDynArray: begin
            if value.TryAsType<TBytes>(bytes) then // TBytes
              valstr := BytesToHex(bytes)
            else
              if value.TryAsType<TArray<TBytes>>(bytes_arr) then // TArray<TBytes>
              begin
                valstr := '';
                for C1 := Low(bytes_arr) to High(bytes_arr) do
                  valstr := valstr + QuotedStr(BytesToHex(bytes_arr[C1])) + ', ';
                if valstr <> '' then
                  Delete(valstr, Length(valstr) - 1, 2);
                valstr := Format('(%s)', [valstr]);
              end
              else
                if value.TryAsType<TArray<UINT32>>(uints) then // TArray<UINT32>
                begin
                  valstr := '';
                  for C1 := Low(uints) to High(uints) do
                    valstr := valstr + IntToStr(uints[C1]) + ', ';
                  if valstr <> '' then
                    Delete(valstr, Length(valstr) - 1, 2);
                  valstr := Format('(%s)', [valstr]);
                end;
          end;
    
          tkUnknown: ;
          tkSet: ;
          tkMethod: ;
          tkVariant: ;
          tkArray: ;
          tkRecord: ;
          tkInterface: ;
          tkClassRef: ;
          tkPointer: ;
          tkProcedure: ;
        end;
    
        propstr := Format('%s: %s', [prop.Name, valstr]);
        fullstr := fullstr + propstr + '; ';
      end;
    
      if fullstr <> '' then
        Delete(fullstr, Length(fullstr) - 1, 2);
    
      result := fullstr;
    end;
    

    【讨论】:

    • 记录和 RTTI 似乎有问题 - 请参阅最近的问题:stackoverflow.com/questions/23445775/…
    • 我的 delphi 不知道类型 tkInteger ... -> 要包含什么单位?
    • tkintger -> 找到 typinfo;
    • @MartynA 这个问题是关于属性而不是字段
    • @David Heffernan,当然,但从省略号来看,OP 似乎并没有排除属性。
    猜你喜欢
    • 1970-01-01
    • 2016-07-11
    • 2013-06-16
    • 2013-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多