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

Delphi多版本泛型查询函数空值类型转换兼容问题求解

问题:Delphi泛型函数RetornaCampoTabela跨版本兼容问题

我编写了一个Delphi泛型函数RetornaCampoTabela<T>,用于从查询结果返回指定类型的值。当查询返回的varCampo为null时,Delphi 10.3会返回0(符合预期),但在Delphi 11.2中出现“invalid type cast”错误。我曾尝试通过判断具体类型来处理,但希望采用通用的泛型实现,而非针对特定类型的分支代码,求修改方案以兼容所有Delphi版本。

原函数代码

class function TDBUtilsController.RetornaCampoTabela<T>(aCamposWhere: array of string; aValuesWhere: array of Variant; ATabela, ARetorno: string): T;
var
  Statement: IDBStatement;
  ResultSet: IDBResultSet;
  varCampo : Variant;
  sSql, sWhere, saux : string;
  I : Integer;
  valResult: TValue;
begin
  if Length(aValuesWhere) <> Length(aCamposWhere) then
  begin
    Application.MessageBox('Parâmetros não conferem', 'Array inválido', MB_OK + MB_ICONERROR);
    Abort;
  end;

  sSql := 'SELECT ' + ARetorno +  ' AS CAMPO FROM ' +  ATabela +  ' ';
  sWhere := ' WHERE ';
  for i := 0 to  Length( aValuesWhere ) -1 do
  begin
    case VarType(aValuesWhere[i]) of
      varString, varUString: saux := QuotedStr( VarToStr( aValuesWhere[i] ) );
    else
      saux := VarToStr( aValuesWhere[i] );
    end;
    if i > 0 then
      sWhere := sWhere + '  AND ' + aCamposWhere[I] + ' = ' +  saux
    else
      sWhere := sWhere + aCamposWhere[I] + ' = ' +  saux ;
  end;
  sSql := sSql + sWhere;
  try
    Statement := AIDBConn.CreateStatement;
    Statement.SetSQLCommand(sSql );
    ResultSet := Statement.ExecuteQuery;
    varCampo  := ResultSet.GetFieldValue('CAMPO');

    valResult := TValue.FromVariant(varCampo);
    Result    := TValue.From<T>( valResult.AsType<T> ).AsType<T>;
  except
    on E: Exception do
    begin
      raise Exception.Create('Erro ao buscar campo: ' + ARetorno  +
           '  da tabela: ' +  ATabela  + sLineBreak + e.Message );
    end;
  end;
end;

曾尝试的非通用方案示例

if varIsNull(varCampo) then
begin
  case getTypeInfo(T) of
    tkInteger: Result := TValue.From<Integer>(0)
  end else
  begin
    Result := TValue.From<T>( valResult.AsType<T> ).AsType<T>;
  end

修改方案(通用泛型实现)

核心思路是利用Default<T>获取泛型类型的默认值,结合TValue.TryAsType<T>做安全类型转换,避免版本差异导致的异常,同时无需针对特定类型写分支代码。

修改后的关键代码段

将原函数中类型转换的部分:

valResult := TValue.FromVariant(varCampo);
Result    := TValue.From<T>( valResult.AsType<T> ).AsType<T>;

替换为:

if VarIsNull(varCampo) then
  // 直接返回对应类型的默认值:数值型0、字符串空串、布尔False等
  Result := Default<T>
else
begin
  valResult := TValue.FromVariant(varCampo);
  // 安全尝试转换类型,失败则返回默认值
  if not valResult.TryAsType<T>(Result) then
    Result := Default<T>;
end;

完整修改后的函数代码

class function TDBUtilsController.RetornaCampoTabela<T>(aCamposWhere: array of string; aValuesWhere: array of Variant; ATabela, ARetorno: string): T;
var
  Statement: IDBStatement;
  ResultSet: IDBResultSet;
  varCampo : Variant;
  sSql, sWhere, saux : string;
  I : Integer;
  valResult: TValue;
begin
  if Length(aValuesWhere) <> Length(aCamposWhere) then
  begin
    Application.MessageBox('Parâmetros não conferem', 'Array inválido', MB_OK + MB_ICONERROR);
    Abort;
  end;

  sSql := 'SELECT ' + ARetorno +  ' AS CAMPO FROM ' +  ATabela +  ' ';
  sWhere := ' WHERE ';
  for i := 0 to  Length( aValuesWhere ) -1 do
  begin
    case VarType(aValuesWhere[i]) of
      varString, varUString: saux := QuotedStr( VarToStr( aValuesWhere[i] ) );
    else
      saux := VarToStr( aValuesWhere[i] );
    end;
    if i > 0 then
      sWhere := sWhere + '  AND ' + aCamposWhere[I] + ' = ' +  saux
    else
      sWhere := sWhere + aCamposWhere[I] + ' = ' +  saux ;
  end;
  sSql := sSql + sWhere;
  try
    Statement := AIDBConn.CreateStatement;
    Statement.SetSQLCommand(sSql );
    ResultSet := Statement.ExecuteQuery;
    varCampo  := ResultSet.GetFieldValue('CAMPO');

    // 通用空值处理与类型转换
    if VarIsNull(varCampo) then
      Result := Default<T>
    else
    begin
      valResult := TValue.FromVariant(varCampo);
      if not valResult.TryAsType<T>(Result) then
        Result := Default<T>;
    end;
  except
    on E: Exception do
    begin
      raise Exception.Create('Erro ao buscar campo: ' + ARetorno  +
           '  da tabela: ' +  ATabela  + sLineBreak + e.Message );
    end;
  end;
end;

方案说明

  • Default<T>:Delphi内置的泛型函数,自动返回对应类型的默认值,完美适配数值、字符串、布尔、引用等各种类型,无需分支判断。
  • TValue.TryAsType<T>:安全尝试将TValue转换为目标类型,转换失败时返回False,不会抛出异常,解决了Delphi 11.2中null值转换报错的问题。
  • 整个实现完全泛型化,无需针对特定类型编写逻辑,兼容所有支持泛型的Delphi版本。

内容的提问来源于stack exchange,提问作者Gilmar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 18:15:35