Delphi - 将 TValue 传递给通用方法
Delphi - pass TValue to generic method
我需要使用 RTTI 遍历具有复杂结构的 class。 class 有几个我也想迭代的记录成员。
TRTTIHelpers<T> = class
public
class function DoGetValuesForClass(aClassInst: T): TStringList;
class function DoGetValuesForRecord(aRec: T): TStringList;
end;
我知道当我在 class 中有成员时,这是一条记录:
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 以便我也可以遍历记录?
使用TValue
的AsType<T>
方法将值转换为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>
可能不应该是通用的 class,而是使用 RTTI 类型为您提供灵活的功能,以便 运行 时间确定的输入。这个建议当然可能是错误的,但我只有问题中的一小段代码来指导我。
我需要使用 RTTI 遍历具有复杂结构的 class。 class 有几个我也想迭代的记录成员。
TRTTIHelpers<T> = class
public
class function DoGetValuesForClass(aClassInst: T): TStringList;
class function DoGetValuesForRecord(aRec: T): TStringList;
end;
我知道当我在 class 中有成员时,这是一条记录:
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 以便我也可以遍历记录?
使用TValue
的AsType<T>
方法将值转换为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>
可能不应该是通用的 class,而是使用 RTTI 类型为您提供灵活的功能,以便 运行 时间确定的输入。这个建议当然可能是错误的,但我只有问题中的一小段代码来指导我。