通过 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 传递给 TRttiPropertySetValue 时,它会执行转换为 属性 的类型,即 Variant - 请参阅 System.Rtti.Conv2Variant。此方法忽略了 tkFloat 可以是 TDateTime 的事实,而是简单地将存储浮点数的 Variant 放入结果中。

解决方案很简单:不要使用 TValue.FromVariant,只需使用 TValue.From<Variant>(value)。这样您就可以将 Variant 值存储在 TValue 中,并按原样传递它,而无需对 setter.

进行任何不必要的隐式类型转换

报告为https://quality.embarcadero.com/browse/RSP-21176