You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 18:04:53