Delphi - передать TValue универсальному методу

Мне нужно перебрать класс, который имеет сложную структуру, используя RTTI. В классе есть несколько членов записи, которые я также хочу повторить.

 TRTTIHelpers<T> = class
  public
    class function DoGetValuesForClass(aClassInst: T): TStringList;
    class function DoGetValuesForRecord(aRec: T): TStringList;
  end;

Я знаю, когда у меня есть член в классе, который является записью:

   for prop in rt.GetProperties() do
    begin
      if prop.PropertyType is TRttiRecordType then
      begin
        lValue := prop.GetValue(aInst);
        Result.AddStrings(TRTTIHelpers<T>.DoGetValuesForRecord(TValue)); <--
      end

Как я могу передать TValue в качестве параметра DoGetValuesForRecord, чтобы я также мог перебирать запись?

1 ответ

Решение

Использовать AsType<T> метод TValue бросить значение в T:

lValue := prop.GetValue(aInst);
Result.AddStrings(TRTTIHelpers<T>.DoGetValuesForRecord(lValue.AsType<T>));

Эта простая программа демонстрирует это:

{$APPTYPE CONSOLE}

uses
  System.RTTI;

type
  TMyRecord = record
    foo: Integer;
  end;

  TMyClass = class
    function GetRec: TMyRecord;
    property Rec: TMyRecord read GetRec;
    function GetInt: Integer;
    property Int: Integer read GetInt;
  end;

function TMyClass.GetRec: TMyRecord;
begin
  Result.foo := 42;
end;

function TMyClass.GetInt: Integer;
begin
  Result := 666;
end;

procedure Main;
var
  inst: TMyClass;
  ctx: TRttiContext;
  typ: TRttiType;
  prop: TRttiProperty;
  value: TValue;
  rec: TMyRecord;
begin
  inst := TMyClass.Create;
  typ := ctx.GetType(TypeInfo(TMyClass));
  for prop in typ.GetProperties do begin
    if prop.Name='Rec' then begin
      value := prop.GetValue(inst);
      rec := value.AsType<TMyRecord>;
      Writeln(rec.foo);
    end;
  end;
end;

begin
  try
    Main;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

Конечно, это требует, чтобы собственность prop имеет правильный тип. Если нет, то вы столкнетесь с ошибкой во время выполнения. В моем примере выше я проверяю имя свойства, чтобы убедиться, что получаю желаемое свойство. Если вы удалите этот тест, то программа завершится с ошибкой во время выполнения EInvalidCast ошибка.

Глядя на ваш код, я подозреваю, что вы, скорее всего, столкнетесь с такими ошибками. Для меня было бы удивительно, если бы каждое свойство rt был того же типа.

Смотря на TRTTIHelpers<T> Я не думаю, что это будет очень полезно для вас в его нынешнем виде. По крайней мере, вы не будете хорошо взаимодействовать с кодом, основанным на RTTI. Причина в том, что призывает TRTTIHelpers<T> требует, чтобы вы указали параметр типа во время компиляции. Но с кодом RTTI вы не знаете этот тип во время компиляции. Я подозреваю что TRTTIHelpers<T> вероятно, не должен быть универсальным классом, а вместо этого использовать типы RTTI, чтобы предложить вам функциональность, гибкую для типизации, определенной во время выполнения. Этот совет, конечно, может быть неправильным, но у меня есть только небольшие фрагменты кода в этом вопросе, чтобы направлять меня.

Другие вопросы по тегам