在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背后的真实对象进行任何“作弊”(出于本问题范围之外的原因)。
答案 0 :(得分:5)
没有为通用类型本身生成RTTI(它们不会在运行时存在),并且每个特定的实例化(如TList<string>
)都是不同的类类型,具有自己的不同RTTI < / em>的。您必须检查每个单独的类型,否则无法测试任何通用类型。解析类名是检测泛型类型的唯一方法。
使用TRttiType.Name
将类名称作为字符串('TList<System.string>'
)。
解析它以检测是否存在尖括号('<>'
)。
提取括号('System.string'
)
走祖先树寻找TRttiType.Name
为'TEnumerable<...>'
的祖先,其中...
是提取的子串('TEnumerable<System.string>'
)。
但是,对于从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类型都不代表枚举。