Reputation: 26650
When I assign a TDateTime
value to OleVariant
property of an object using RTTI, object becomes Float value.
The object is designed so that this property can become either Null or value of any data type. If it becomes float, then result should be calculated as a difference of floats. If it becomes TDateTime
, then result should be calculated as a difference of two TDateTime
values.
Would I have passed the value directly to it, it will work fine, but there is RTTI in the middle.
I know that TDateTime
is internally repersented as float, but is there a possibility to receive exactly the data type that I send?
Please look at the tkVariant
case in following sample of code:
class procedure TRTTI.SetObjPropValue(obj: TObject; rprop: TRttiProperty; value: OleVariant);
var
rtyp: TRttiType;
vt: TVarType;
begin
if obj = nil then Exit();
if (rprop <> nil) and (rprop.IsWritable) then begin
case rprop.PropertyType.TypeKind of
tkInteger, tkInt64:
begin
value := TVarConv.NullableCurr(value);
if VarIsNumeric(value) then rprop.SetValue(obj, TValue.FromVariant(Trunc(value)));
end;
tkFloat:
begin
if rprop.PropertyType.Name = 'TDateTime' then
value := TVarConv.NullableDateTime(value)
else
value := TVarConv.NullableFloat(value);
if not VarIsNull(value) then rprop.SetValue(obj, TValue.FromVariant(value));
end;
tkChar, tkString, tkWChar, tkLString, tkWString, tkUString:
begin
rprop.SetValue(obj, TValue.FromVariant(VarToStr(value)));
end;
tkEnumeration:
begin
if rprop.PropertyType.Name = 'Boolean' then
value := TVarConv.NullableBool(value)
else
value := null;
if not VarIsNull(value) then rprop.SetValue(obj, TValue.FromVariant(value));
end;
tkVariant:
//Here I transmit the TDateTime value
rprop.SetValue(obj, TValue.FromVariant(value));
//An object receives Float
end;
end;
end;
Upvotes: 2
Views: 424
Reputation: 21713
The issue here is that TValue.FromVariant
is internally "unpacking" the passed Variant
, storing the underlying value inside the TValue
. In your case it properly notices that a TDateTime
is stored in the Variant
and stores it as TDateTime
.
When you pass that TValue
containing a TDateTime
(which is of TypeKind tkFloat
) down to the SetValue
of the TRttiProperty
it does a conversion to the type of the property which is Variant
- see System.Rtti.Conv2Variant
. This method ignores the fact that a tkFloat
can be a TDateTime
but simply puts a Variant
that stores a float into the result.
The solution is simple: don't use TValue.FromVariant
but simply TValue.From<Variant>(value)
. That way you are storing the Variant value inside the TValue and pass it as it is without any unnecessary implicit type conversions to the setter.
Reported as https://quality.embarcadero.com/browse/RSP-21176
Upvotes: 6