如何测试未知的Delphi RTTI TValue是否反映了任何类型的通用TList<> (或至少TEnumerable<>)?

时间:2015-03-08 02:59:45

标签: delphi generics rtti typechecking

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

注意:我已经知道如何轻松检查其完全类型,即使用.BaseType TRttiType的{​​{1}}属性,例如导致TValue,但我要测试的是,如果它是任何子项目类型的TList<string>

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

TList<>

同样,如果我还能检测到哪种<{em>类型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<>

我试过了:

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,但除了手动解析此字符串以实现我的目标之外,确实必须有其他方式,对吧?

最后,目标必须仅使用RTTI信息完成,也就是说,不允许通过引用TValue背后的真实对象进行任何“作弊”(出于本问题范围之外的原因)。

1 个答案:

答案 0 :(得分:5)

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

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

  2. 解析它以检测是否存在尖括号('<>')。

  3. 提取括号('System.string'

  4. 之间的子串
  5. 走祖先树寻找TRttiType.Name'TEnumerable<...>'的祖先,其中...是提取的子串('TEnumerable<System.string>')。

    < / LI>

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

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

    要考虑到这一点,请忽略步骤1-3并单独跳到第4步,忽略括号之间出现的任何值,例如:

    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,它代表enumerated types(即:type typeName = (val1, ...,valn);)。它与TEnumerable<T>无关。这就是is运算符总是为您返回False的原因 - 您正在测试的RTTI类型都不代表枚举。