如何获取TObjectList< T>的子项类型纯粹是由Delphi中的RTTI信息(即不使用任何实际的对象实例)? [英] How can I get the sub-item type of a TObjectList<T> purely by RTTI information (i.e. without using any actual object instance) in Delphi?

查看:210
本文介绍了如何获取TObjectList< T>的子项类型纯粹是由Delphi中的RTTI信息(即不使用任何实际的对象实例)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm implementing generic code for streaming arbitrary Delphi objects using RTTI, and in order to get this to work (more specifically, in order to get the loading part to work), I need to somehow get the sub-item type of a TObjectList<T> field without making use of any actual object instance.

需求的明显原因不使用任何实际的对象实例是在从流中加载对象的情况下(仅基于要加载的对象的类类型的知识),在加载之前,我将不会有任何实例可用已经完成了 - 我宁愿只能访问有关课程的纯RTTI数据。

The obvious reason for the requirement to not use any actual object instance is that in the case of loading an object from a stream (based solely on the knowledge of the class type of the object to be loaded), I won't have any instance at all available before the loading has completed - I will rather only have access to the pure RTTI data of the class in question.

我想要的一个类的一个例子加载如下:

An example of such a class that I'd like to be able to load is the following:

TTestClass = class(TObject)
public
   test_list : TList<string>;
end;

我想要的是可以得出结论, test_list 字段是一个通用的 TList< T> 其中 T string (即为了知道从子流项目中可以得到什么样的数据)。

What I want is to be able to conclude that the test_list field is a generic TList<T> where T is string (i.e. in order to know what data to expect from the stream for the sub-items).

如果类的代码看起来如下: / p>

If the class did instead look as follows:

TTestClassWithArr = class(TObject)
public
   test_arr : array of string;
end;

我可以使用 ElementType()方法的 TRttiDynamicArrayType RTTI类的 test_arr 字段,以纯粹通过RTTI提取此信息,但我找不到任何相应的另外一个堆栈溢出问题(

I could use the ElementType() method of the TRttiDynamicArrayType RTTI class of the test_arr field to extract this information purely through RTTI, but I cannot find any corresponding such explicit RTTI type for TObjectList<T>.

/stackoverflow.com/questions/12497843/delphi-rtti-how-to-get-objects-from-tobjectlistt\"> Delphi Rtti:如何从 TObjectList< T> )是相关的,但确实使用RTTI数据反映为欺骗的对象的实际实例,以便获得子项目,这再次对我来说不是一个选项,因为这些在我必须知道的时候,子项目不存在。

Another Stack Overflow question (Delphi Rtti: how to get objects from TObjectList<T>) is related, but does indeed use an actual instance of the object that the RTTI data reflects to "cheat" in order to get to the sub-items, which, again, is not an option for me since these sub-items do not exist at the time I must know this.

真的觉得应该有一些方法可以通过使用类的RTTI信息来做到这一点尽管如此,由于所有的类型信息在编译时显然是存在的,

It really feels like there should be some way to do this by solely using the RTTI information of the class though, since all the type information is obviously present for it at compile-time, regardless of object instantiation.

推荐答案

不幸的是,没有为Generic参数生成RTTI。在通用容器(如 TList )中发现 T 的值的唯一方法是获取 TRttiType 为目标字段本身,调用其 ToString()方法获取其类名作为字符串,并解析出括号之间的子串。例如:

Unfortunately, there is no RTTI generated for Generic parameters. The only way to discover the value of T in a Generic container like TList<T> is to get the TRttiType for the target field itself, call its ToString() method to get its class name as a string, and parse out the substring that is between the brackets. For example:

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参数解压缩为一个字符串后,可以使用 TRttiContext .FindType()如果您需要访问该类型的RTTI。

Once you have extracted the Generic parameter as a string, you can use TRttiContext.FindType() if you need to access the RTTI for that type.

就是说,下面的代码提供了一大堆RTTI帮助:

With that said, the following code provides a bunch of RTTI helpers:

DSharp.Core.Reflection.pas (Google代码)

DSharp.Core.Reflection.pas (Google Code)

DSharp.Core.Reflection.pas (BitBucket)

DSharp.Core.Reflection.pas (BitBucket)

除此之外,它定义了一个 TRttiTypeHelper 类助手,它添加了一个 GetGenericArguments()方法到 TRttiType

Amongst other things, it defines a TRttiTypeHelper class helper that adds a GetGenericArguments() method to TRttiType:

TRttiTypeHelper = class helper for TRttiType
...
public 
  ...
  function GetGenericArguments: TArray<TRttiType>;
  ...
end;

在内部, GetGenericArguments()使用相同技术我上面提到使用它,您可以这样做:

Internally, GetGenericArguments() uses the same technique I mention above. With it, you can do this instead:

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;

这篇关于如何获取TObjectList&lt; T&gt;的子项类型纯粹是由Delphi中的RTTI信息(即不使用任何实际的对象实例)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆