要求不使用任何实际对象实例的明显原因是,在从流加载对象的情况下(仅基于要加载的对象的类类型的知识),我将不会有任何实例在加载完成之前完全可用 – 我宁愿只能访问相关类的纯RTTI数据.
我希望能够加载的这样一个类的示例如下:
TTestClass = class(TObject) public test_list : TList<string>; end;
我想要的是能够得出结论test_list字段是通用TList< T>.其中T是字符串(即,为了知道从子流的流中期望什么数据).
如果该课程反而如下:
TTestClassWithArr = class(TObject) public test_arr : array of string; end;
我可以使用test_arr字段的TRttiDynamicArrayType RTTI类的Elementtype()方法纯粹通过RTTI提取此信息,但是我找不到TObjectList< T>的任何相应的这种显式RTTI类型.
另一个Stack Overflow问题(Delphi Rtti: how to get objects from TObjectList<T>)是相关的,但确实使用了RTTI数据反映的对象的实际实例来“欺骗”以获取子项,这对我来说也不是一个选项.这些子项目在我必须知道的时候不存在.
通过单独使用类的RTTI信息,确实应该有某种方法来实现这一点,因为无论对象实例化如何,所有类型信息显然都是在编译时为它提供的.
解决方法
uses
...,System.StrUtils,System.Rtti;
var
Ctx: TRttiContext;
s: string;
OpenBracket,CloseBracket: Integer;
...
begin
...
s := Ctx.GetType(TTestClass).GetField('test_list').FieldType.ToString; // returns 'TList<System.string>'
OpenBracket := Pos('<',s);
CloseBracket := PosEx('>',s,OpenBracket+1);
s := copy(s,OpenBracket+1,CloseBracket-OpenBracket-1); // returns 'System.string'
// if multiple Generic parameters are present,they will be separated by commas...
...
end;
将Generic参数解压缩为字符串后,如果需要访问该类型的RTTI,则可以使用TRttiContext.FindType().
话虽如此,下面的代码提供了一堆RTTI助手:
DSharp.Core.Reflection.pas(谷歌代码)
DSharp.Core.Reflection.pas(BitBucket)
除此之外,它定义了一个TRttiTypeHelper类助手,它将一个GetGenericArguments()方法添加到TRttiType:
TRttiTypeHelper = class helper for TRttiType ... public ... function GetGenericArguments: TArray<TRttiType>; ... end;
在内部,GetGenericArguments()使用我在上面提到的相同技术.有了它,你可以这样做:
uses
...,System.Rtti,DSharp.Core.Reflection;
var
Ctx: TRttiContext;
arr: TArray<TRttiType>;
typ: TRttiType;
s: string;
...
begin
...
arr := Ctx.GetType(TTestClass).GetField('test_list').FieldType.GetGenericArguments;
typ := arr[0]; // returns RTTI for System.String
s := typ.ToString; // returns 'System.string'
...
end;