要求不使用任何实际对象实例的明显原因是,在从流加载对象的情况下(仅基于要加载的对象的类类型的知识),我将不会有任何实例在加载完成之前完全可用 – 我宁愿只能访问相关类的纯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;