通过 RTTI 将 TDateTime 值传递给 OleVariant 属性
Pass TDateTime value to OleVariant property through RTTI
当我使用 RTTI 将 TDateTime
值分配给对象的 OleVariant
属性 时,对象变为 Float 值。
对象的设计使得此 属性 可以变为 Null 或任何数据类型的值。如果它变成浮点数,那么结果应该计算为浮点数的差值。如果它变为 TDateTime
,则结果应计算为两个 TDateTime
值的差值。
我会不会直接传值给它,它可以正常工作,但是中间有RTTI。
我知道 TDateTime
在内部表示为浮点数,但是否有可能准确接收我发送的数据类型?
请查看以下代码示例中的 tkVariant
案例:
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;
这里的问题是 TValue.FromVariant
在内部 "unpacking" 传递的 Variant
,将基础值存储在 TValue
中。在您的情况下,它正确地注意到 TDateTime
存储在 Variant
中并将其存储为 TDateTime
.
当您将包含 TDateTime
(属于 TypeKind tkFloat
)的 TValue
传递给 TRttiProperty
的 SetValue
时,它会执行转换为 属性 的类型,即 Variant
- 请参阅 System.Rtti.Conv2Variant
。此方法忽略了 tkFloat
可以是 TDateTime
的事实,而是简单地将存储浮点数的 Variant
放入结果中。
解决方案很简单:不要使用 TValue.FromVariant
,只需使用 TValue.From<Variant>(value)
。这样您就可以将 Variant 值存储在 TValue 中,并按原样传递它,而无需对 setter.
进行任何不必要的隐式类型转换
当我使用 RTTI 将 TDateTime
值分配给对象的 OleVariant
属性 时,对象变为 Float 值。
对象的设计使得此 属性 可以变为 Null 或任何数据类型的值。如果它变成浮点数,那么结果应该计算为浮点数的差值。如果它变为 TDateTime
,则结果应计算为两个 TDateTime
值的差值。
我会不会直接传值给它,它可以正常工作,但是中间有RTTI。
我知道 TDateTime
在内部表示为浮点数,但是否有可能准确接收我发送的数据类型?
请查看以下代码示例中的 tkVariant
案例:
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;
这里的问题是 TValue.FromVariant
在内部 "unpacking" 传递的 Variant
,将基础值存储在 TValue
中。在您的情况下,它正确地注意到 TDateTime
存储在 Variant
中并将其存储为 TDateTime
.
当您将包含 TDateTime
(属于 TypeKind tkFloat
)的 TValue
传递给 TRttiProperty
的 SetValue
时,它会执行转换为 属性 的类型,即 Variant
- 请参阅 System.Rtti.Conv2Variant
。此方法忽略了 tkFloat
可以是 TDateTime
的事实,而是简单地将存储浮点数的 Variant
放入结果中。
解决方案很简单:不要使用 TValue.FromVariant
,只需使用 TValue.From<Variant>(value)
。这样您就可以将 Variant 值存储在 TValue 中,并按原样传递它,而无需对 setter.