如何测试未知的Delphi RTTI TValue是否反映了任何类型的通用TList的对象, (或至少TEnumerable)? [英] How can I test if an unknown Delphi RTTI TValue reflects an object that is ANY type of generic TList<> (or at least TEnumerable<>)?

查看:185
本文介绍了如何测试未知的Delphi RTTI TValue是否反映了任何类型的通用TList的对象, (或至少TEnumerable)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Delphi中,如果我有一个反映未知对象的 TValue 实例,那么我如何测试这个对象是否是任何类型的通用或者甚至更好,也是 特定的通用枚举类型,它是一个例子,例如 TList<> )?

In Delp if I have a TValue instance reflecting an unknown object, how can I test if this object is an instance of ANY kind of generic TEnumerable<> (or even better, also which specific generic enumerable type it is an instance of, e.g. TList<>)?

注意:我已经知道如何轻松查看其确切的类型,即使用 .BaseType 属性 TRttiType TValue ,导致例如 TList< string> ,但是我想要测试的是,如果它是 TList em>任何子项类型

NOTE: I already know how to easily check its exact type, i.e. with the .BaseType property of the corresponding TRttiType of the TValue, resulting in for example TList<string>, but what I want to test is rather if it is a TList<> of any sub-item type.

为了说明这个假设代码IsAnyKindOfGenericEnumerable()如何工作,这里是一些示例代码:

To exemplify how this hypothetical code "IsAnyKindOfGenericEnumerable()" would work, here is some example code:

var
   LContext : TRttiContext;
   obj_1_rtti_value : TValue;
   obj_2_rtti_value : TValue;
   obj_3_rtti_value : TValue;
   obj_1_rtti_type : TRttiType;
   obj_2_rtti_type : TRttiType;
   obj_3_rtti_type : TRttiType;

LContext := TRttiContext.Create();

{
...
obj_1_rtti_value is set to a TValue reflection of a TList<string> object here
obj_2_rtti_value is set to a TValue reflection of a plain TObject object here
obj_3_rtti_value is set to a TValue reflection of a TQueue<integer> object here
...
}

obj_1_rtti_type := LContext.GetType(obj_1_rtti_value.TypeInfo);
obj_2_rtti_type := LContext.GetType(obj_2_rtti_value.TypeInfo);
obj_3_rtti_type := LContext.GetType(obj_3_rtti_value.TypeInfo);

IsAnyKindOfGenericEnumerable(obj_1_rtti_type); //Would return true
IsAnyKindOfGenericEnumerable(obj_2_rtti_type); //Would return false
IsAnyKindOfGenericEnumerable(obj_3_rtti_type); //Would return true

再次,最好的事情是,如果我也可以检测到> 类型 TEnumerable<> 键入,就像例如:

And again, the very best thing would be if I could also detect which kind of TEnumerable<> type it is, like for example:

IsAnyKindOfGenericEnumerable(obj_1_rtti_type); //Will return true + `TList<>`
IsAnyKindOfGenericEnumerable(obj_2_rtti_type); //Will return false
IsAnyKindOfGenericEnumerable(obj_3_rtti_type); //Will return true + `TQueue<>`

我尝试过:

if obj_1_rtti_type is TRttiEnumerationType then
begin
   //...
end;

但是由于某种原因,它评估为 false ,我完全失去了为什么呢?在这种情况下,表达式 value_type.BaseType.Name 确实评估为'TEnumerable< System.string>'为了完成我的目标,真的需要一些其他的方式来手动解析这个字符串,对吗?

but for some reason this evaluates to false, which I'm completely at loss as to why that is? The expression value_type.BaseType.Name does indeed evaluate to 'TEnumerable<System.string>' in this case, but there really has to be some other way than to manually parse this string in order to accomplish my objective, right?

最后,目标必须完全使用RTTI信息也就是说,通过引用TValue背后的真实对象,不允许任何欺骗(由于这个问题范围之外的原因)。

Finally, the goal must be accomplished solely using the RTTI info, that is, any "cheating" by referring to the real object behind the TValue is not permitted (for reasons outside the scope of this question).

推荐答案

对于Generic类型本身没有生成RTTI(它们在运行时不存在),并且每个特定的实例化(如 TList< string> )是一个具有自己独特的RTTI 的不同类型。您必须检查每个单独的类型,不可能测试任何通用类型。解析类名是检测Generic类型的唯一方法。

There is no RTTI generated for Generic types themselves (they don't exist at runtime), and each specific instantiation (like TList<string>) is a distinct class type with its own distinct RTTI. You would have to check for each individual type, it is not possible to test for any Generic type. Parsing class names is the only way to detect Generic types.


  1. 使用 TRttiType.Name 将类名称作为字符串('TList< System.string>')。

  1. use TRttiType.Name to get the class name as a string ('TList<System.string>').

解析它以检测尖括号('<>')的存在。

parse it to detect the presence of angle brackets ('<>').

在括号之间提取子串('System.string'

extract the substring between the brackets ('System.string')

树寻找一个祖先,他的 TRttiType.Name 'TEnumerable< ...>',其中 ... 是提取的子字符串('TEnumerable< System.string>')。

walk the ancestor tree looking for an ancestor whose TRttiType.Name is 'TEnumerable<...>', where ... is the extracted substring ('TEnumerable<System.string>').

但是,对于从 TEnumerable 派生的类类型,此方法失败,但没有泛型参数本身,例如:

However, this approach fails for class types that derive from TEnumerable<T> but do not have Generics parameters themselves, eg:

type
  TMyClass = class(TEnumerable<string>)
  end;

为了解决这个问题,请忽略第1-3步,直接跳到步骤4,忽略任何值出现在括号之间,例如:

To account for that, ignore steps 1-3 and jump right to step 4 by itself, ignoring whatever value appears between the brackets, eg:

function IsAnyKindOfGenericEnumerable(AType: TRttiType): Boolean;
begin
  Result := False;
  while AType <> nil do
  begin
    Result := StartsText('TEnumerable<', AType.Name);
    if Result then Exit;
    AType := AType.BaseType;
  end;
end;

对于 TRttiEnumerationType ,它代表 枚举类型 (即: type typeName =(val1,...,valn); )。它与 TEnumerable< T> 无关。这就是为什么操作符总是为你返回False - 没有一个被测试的RTTI类型表示枚举。

As for TRttiEnumerationType, it represents enumerated types (ie: type typeName = (val1, ...,valn);). It has nothing to do with TEnumerable<T>. That is why the is operator is always returning False for you - none of the RTTI types you are testing represent enums.

这篇关于如何测试未知的Delphi RTTI TValue是否反映了任何类型的通用TList的对象, (或至少TEnumerable)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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