Reputation: 12584
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
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