I have this code below, the var name varCampo
when returned null causes an "invalid type cast" error when using different versions of Delphi. I have tried 10.3 and 11.2.
In Delphi 10.3, when using, eg:
var codigo: integer;
codigo := RetornaCampoTabela<integer>(['a'], ['teste'], 'description', 'Id');
When executing the code and varCampo = null
, Delphi 10.3 returns 0 (zero), which is correct, but in Delphi 11.2 I get "INVALID TYPE CAST".
How to solve this code so it works in all versions of 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;
I tried to use case
for detecting the datatype T
and return according to T
passed into the function, eg:
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
I don't want this type of code, I want generic code.
Why are you involving TValue
at all? The compiler already knows how to implicitly convert Variant
to various types, including integers. If varCampo
is null
, just return Default(T)
instead, otherwise return the Variant
as-is and let the compiler decide how to convert it, eg:
varCampo := ResultSet.GetFieldValue('CAMPO');
if VarIsNull(varCampo) then
Result := Default(T)
else
Result := varCampo;
UPDATE: apparently the compiler doesn't like the latter implicit conversion. So, you are right, you will have to go through TValue
. However, your usage is too verbose, you don't need to create a 2nd TValue
from a value you have already converted from another TValue
. Your usage of TValue
can be simplified:
if varIsNull(varCampo) then begin
Result := Default(T);
end else
begin
valResult := TValue.FromVariant(varCampo);
Result := valResult.AsType<T>;
end;
Or simpler:
if varIsNull(varCampo) then
Result := Default(T)
else
Result := TValue.FromVariant(varCampo).AsType<T>;