Que*_*low 3 delphi generics rtti typechecking
在Delphi中,如果我有一个TValue反映未知对象的实例,我该如何测试这个对象是否是任何类型的通用实例TEnumerable<>(甚至更好,还有哪个特定的通用可枚举类型,例如TList<>)?
注:我已经知道如何轻松地检查它的确切类型,即具有.BaseType相应的财产TRttiType的TValue,造成例如TList<string>,但我想测试比较,如果它是TList<>的任何分项类型.
为了举例说明这个假设代码"IsAnyKindOfGenericEnumerable()"如何工作,这里有一些示例代码:
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
Run Code Online (Sandbox Code Playgroud)
而且,如果我还能检测出它是哪种类型,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<>`
Run Code Online (Sandbox Code Playgroud)
我试过了:
if obj_1_rtti_type is TRttiEnumerationType then
begin
//...
end;
Run Code Online (Sandbox Code Playgroud)
但由于某种原因,这个评估false,我完全不知道为什么会这样?在这种情况下,表达式value_type.BaseType.Name确实会评估'TEnumerable<System.string>',但实际上必须有一些其他方法,而不是手动解析此字符串以实现我的目标,对吧?
最后,目标必须仅使用RTTI信息来完成,也就是说,不允许通过引用TValue背后的真实对象进行任何"作弊"(出于本问题范围之外的原因).
没有为通用类型本身生成RTTI(它们在运行时不存在),并且每个特定实例化(例如TList<string>)是具有其自己的不同RTTI的不同类类型.您必须检查每种类型,无法测试任何通用类型.解析类名是检测泛型类型的唯一方法.
用于TRttiType.Name将类名称作为字符串('TList<System.string>').
解析它以检测尖括号('<>')的存在.
提取括号之间的子串('System.string')
走祖先树寻找一个祖先,其TRttiType.Name是'TEnumerable<...>',这里...是所提取的子('TEnumerable<System.string>').
但是,对于派生自TEnumerable<T>但没有泛型参数的类类型,此方法失败,例如:
type
TMyClass = class(TEnumerable<string>)
end;
Run Code Online (Sandbox Code Playgroud)
要考虑到这一点,请忽略步骤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;
Run Code Online (Sandbox Code Playgroud)
至于TRttiEnumerationType它,它代表枚举类型(即:) type typeName = (val1, ...,valn);.它与此无关TEnumerable<T>.这就是为什么is运算符总是为您返回False的原因 - 您正在测试的RTTI类型都不代表枚举.
| 归档时间: |
|
| 查看次数: |
928 次 |
| 最近记录: |