我需要使用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,以便我也可以遍历记录?
答案 0 :(得分:7)
使用TValue
的{{3}}方法将值转换为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>
需要您在编译时提供type参数。但是使用RTTI代码,您在编译时就不知道该类型。我怀疑TRTTIHelpers<T>
可能不应该是泛型类,而是使用RTTI类型为您提供灵活的运行时确定类型的功能。这个建议当然可能是错误的,但我只是在问题中有一小段代码来指导我。