access all elements of a record using RTTI

元气小坏坏 提交于 2019-11-29 15:24:19

As starting point - record with simple types. For complex fields (array, class etc) explore RTTI unit

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

output:

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

Here is my try at this. I have similar task as you (refer to this thread). It's work in progress, but does the job good enough so far. This would enumerate all properties inside the TObject thou, so you'll have to adapt it to enumerate records:

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;
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!