Delphi RTTI赋值记录类型时触发'Invalid class typecast'错误求助
Delphi记录类型Implicit运算符与RTTI赋值冲突的解决方案
问题根源
Delphi中定义的Implicit运算符是编译期语法糖,仅在编译阶段生效。RTTI在运行时处理类型赋值时,只会识别原生类型的兼容性,无法自动调用记录的自定义转换逻辑,因此直接通过RTTI将记录类型赋值给其他类型属性时,会触发Invalid class typecast异常。
解决方案:手动识别并调用转换运算符
在通用映射类的RTTI遍历逻辑中,增加对记录类型的判断,通过RTTI查找并调用记录的Implicit转换方法,将记录值转为目标类型后再赋值。
修改前的出错代码
type TMyDouble = record private FValue: Double; public class operator Implicit(AValue: Double): TMyDouble; class operator Implicit(ARecord: TMyDouble): Double; property Value: Double read FValue write FValue; end; class operator TMyDouble.Implicit(AValue: Double): TMyDouble; begin Result.FValue := AValue; end; class operator TMyDouble.Implicit(ARecord: TMyDouble): Double; begin Result := ARecord.FValue; end; procedure TMapper.Map(Source, Target: TObject); var SourceCtx, TargetCtx: TRttiContext; SourceType, TargetType: TRttiType; SourceProp, TargetProp: TRttiProperty; begin SourceCtx := TRttiContext.Create; TargetCtx := TRttiContext.Create; try SourceType := SourceCtx.GetType(Source.ClassType); TargetType := TargetCtx.GetType(Target.ClassType); for TargetProp in TargetType.GetProperties do begin SourceProp := SourceType.GetProperty(TargetProp.Name); if Assigned(SourceProp) then begin // 异常触发行:直接赋值不兼容类型 TargetProp.SetValue(Target, SourceProp.GetValue(Source)); end; end; finally SourceCtx.Free; TargetCtx.Free; end; end;
修改后的修复代码
procedure TMapper.Map(Source, Target: TObject); var SourceCtx, TargetCtx: TRttiContext; SourceType, TargetType: TRttiType; SourceProp, TargetProp: TRttiProperty; SourceValue: TValue; ConvertMethod: TRttiMethod; ConvertedValue: TValue; begin SourceCtx := TRttiContext.Create; TargetCtx := TRttiContext.Create; try SourceType := SourceCtx.GetType(Source.ClassType); TargetType := TargetCtx.GetType(Target.ClassType); for TargetProp in TargetType.GetProperties do begin SourceProp := SourceType.GetProperty(TargetProp.Name); if Assigned(SourceProp) then begin SourceValue := SourceProp.GetValue(Source); // 处理记录类型的自定义转换 if SourceValue.Kind = tkRecord then begin // 查找匹配目标类型的Implicit转换方法 ConvertMethod := SourceValue.TypeInfo.GetType.GetMethod( 'Implicit', [SourceValue.TypeInfo, TargetProp.PropertyType.Handle] ); if Assigned(ConvertMethod) then begin // 调用转换方法得到目标类型值 ConvertedValue := ConvertMethod.Invoke(EmptyParam, [SourceValue]); TargetProp.SetValue(Target, ConvertedValue); end else begin // 无匹配转换时尝试直接赋值(可根据需求改为抛出异常) TargetProp.SetValue(Target, SourceValue); end; end else begin // 非记录类型直接赋值 TargetProp.SetValue(Target, SourceValue); end; end; end; finally SourceCtx.Free; TargetCtx.Free; end; end;
补充说明
- 上述代码通过
TRttiMethod查找记录类的Implicit静态方法,匹配签名为接收当前记录类型,返回目标属性类型的转换逻辑。 - 如果记录定义了多方向的
Implicit运算符,可根据源/目标类型的组合调整方法查找的参数顺序。 - 可以将类型转换逻辑封装为独立函数,进一步简化映射类的代码结构。
内容的提问来源于stack exchange,提问作者Rick Wheeler
相关产品推荐
相关产品推荐

