RBA
RBA

Reputation: 12584

Delphi - pass TValue to generic method

I need to iterate through a class which has a complex structure using RTTI. The class has several record members which I also want to iterate.

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

I know when I have a member in the class which is a record:

   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

How can I pass the TValue as a parameter to DoGetValuesForRecord so I can also iterate through the record?

Upvotes: 2

Views: 1519

Answers (1)

David Heffernan
David Heffernan

Reputation: 613003

Use the AsType<T> method of TValue to cast the value to T:

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

This simple program demonstrates this:

{$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.

Of course, this does require that the property prop is of the correct type. If not then you will encounter a run time error. In my example above, I ensure that is so by testing the name of the property to make sure I get the desired property. If you remove that test then the program fails with a run time EInvalidCast error.

Looking at your code I suspect that you are quite likely to encounter such errors. It would be surprising to me if every single property of rt was of the same type.

Looking at TRTTIHelpers<T> I don't think it will be very useful to you in its current form. At least, you it won't interact well with code based on RTTI. The reason being that calls to TRTTIHelpers<T> require you to supply the type parameter at compile time. But with RTTI code, you don't know that type at compile time. I suspect that TRTTIHelpers<T> should probably not be a generic class, and instead use RTTI types to offer you functionality that is flexible for run time determined typing. This advice could of course be wrong, but I only have the small excerpts of code in the question to guide me.

Upvotes: 7

Related Questions