如何在Delphi中纯粹通过RTTI信息(即不使用任何实际对象实例)获取TObjectList的子项类型?

我正在使用RTTI实现用于流式传输任意Delphi对象的通用代码,并且为了使其工作(更具体地说,为了使加载部分工作),我需要以某种方式获得TObjectList的子项类型&lt ; T>不使用任何实际对象实例的字段.

要求不使用任何实际对象实例的明显原因是,在从流加载对象的情况下(仅基于要加载的对象的类类型的知识),我将不会有任何实例在加载完成之前完全可用 – 我宁愿只能访问相关类的纯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信息,确实应该有某种方法来实现这一点,因为无论对象实例化如何,所有类型信息显然都是在编译时为它提供的.

解决方法

不幸的是,没有为通用参数生成RTTI.在像TList< T>这样的通用容器中发现T值的唯一方法.是获取目标字段本身的TRttiType,调用其ToString()方法将其类名称作为字符串,并解析括号之间的子字符串.例如:
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;

相关文章

 从网上看到《Delphi API HOOK完全说明》这篇文章,基本上都...
  从网上看到《Delphi API HOOK完全说明》这篇文章,基本上...
ffmpeg 是一套强大的开源的多媒体库 一般都是用 c/c+&#x...
32位CPU所含有的寄存器有:4个数据寄存器(EAX、EBX、ECX和ED...
1 mov dst, src dst是目的操作数,src是源操作数,指令实现的...