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
相关产品推荐
相关产品推荐

