Delphi泛型作为参数编译失败:自定义JSON拦截器RTTI操作报错
编译报错原因
Delphi的泛型是编译期静态展开的技术,泛型参数必须在编译阶段就确定具体类型,而你代码里用到的Fld.ClassType、参数C: TClass都是运行时才能拿到的动态值,编译器无法在编译阶段提前为所有可能的类生成对应的TObjectList<T>实例化代码,因此直接将运行时变量作为泛型参数传入会触发编译错误。
通用实现方案
你可以通过RTTI动态构造对应泛型类型的TObjectList实例,不需要手动逐个判断类型写重复代码,实现逻辑如下:
核心代码示例
uses System.Rtti, System.Generics.Collections; procedure TqBitObjectListInterceptor.StringReverter(Data: TObject; Field, Arg: string); var ctx: TRttiContext; objType: TRttiType; targetField: TRttiField; genericListDef: TRttiType; concreteListType: TRttiType; listCtor: TRttiMethod; listInstance: TObject; begin ctx := TRttiContext.Create; try // 获取当前对象的类型信息 objType := ctx.GetType(Data.ClassType); // 查找目标字段 targetField := objType.GetField(Field); if not Assigned(targetField) then Exit; // 如果字段本身已经声明为TObjectList<T>类型,直接用字段的类型信息创建实例 if targetField.FieldType.IsGenericType and (targetField.FieldType.GetGenericTypeDefinition = TypeInfo(TObjectList<>)) then begin concreteListType := targetField.FieldType; end else begin // 如果字段是通用TObject/TList类型,需要传入元素类型构造泛型,可配合映射表使用 // 示例:假设已经通过映射拿到元素类型elementTypeInfo: PTypeInfo // genericListDef := ctx.GetType(TypeInfo(TObjectList<>)); // concreteListType := genericListDef.MakeGenericType([ctx.GetType(elementTypeInfo)]); Exit; end; // 查找带OwnsObjects参数的构造方法 listCtor := concreteListType.GetMethod('Create', [TypeInfo(Boolean)]); if not Assigned(listCtor) then Exit; // 动态调用构造方法创建实例 listInstance := listCtor.Invoke( concreteListType.AsInstance.MetaclassType, [True] ).AsObject; // 给字段赋值 targetField.SetValue(Data, listInstance); finally // 注意:TRttiContext是记录类型,无需手动Free,手动释放反而会触发内存错误 // 同样TRttiType等RTTI对象由框架管理生命周期,不需要手动释放 end; end;
简化调用的改造方案
如果你需要兼容没有提前声明泛型字段的场景,可以沿用你之前的调用思路,封装成通用方法后只需要新增一行配置即可扩展:
procedure TqBitObjectListInterceptor.StringReverter(Data: TObject; Field, Arg: string); function TryAssignList(ElementClassType: PTypeInfo): Boolean; var ctx: TRttiContext; targetField: TRttiField; genericListDef: TRttiType; concreteListType: TRttiType; listCtor: TRttiMethod; listInstance: TObject; begin Result := False; targetField := ctx.GetType(Data.ClassType).GetField(Field); if not Assigned(targetField) then Exit; genericListDef := ctx.GetType(TypeInfo(TObjectList<>)); concreteListType := genericListDef.MakeGenericType([ctx.GetType(ElementClassType)]); listCtor := concreteListType.GetMethod('Create', [TypeInfo(Boolean)]); if not Assigned(listCtor) then Exit; listInstance := listCtor.Invoke(concreteListType.AsInstance.MetaclassType, [True]).AsObject; targetField.SetValue(Data, listInstance); Result := True; end; begin if (Data is TqBitLogsType) and (Field = 'Flogs') then TryAssignList(TypeInfo(TqBitLogType)) else if (Data is TqBitPrefsType) and (Field = 'Flist') then TryAssignList(TypeInfo(TqBitPrefsType)); // 后续新增类型只需要加一行判断即可,无需重复编写创建赋值逻辑 end;
内容的提问来源于stack exchange,提问作者Laurent Meyer
相关产品推荐
相关产品推荐

