如何使用 RTTI 分配 OleVariant? // 将 OleVariant 或 Variant 转换为具有特定 TTypeKind 或 TRTTIType 的 TValue?

How to assign an OleVariant with RTTI? // Convert an OleVariant or a Variant to a TValue with specific TTypeKind or TRTTIType in mind?

我有一个 OleVariantVariant 值,例如,用 IXMLNode.GetAttributeNS 读取,使其成为 "String"(varOleStrvarString),我想写那个值,例如 TRTTIField.SetValue,需要 TValue 分配兼容 TRTTIField.FieldType: TRTTIType

对于基本类型(以及 TVarTypeTRTTIType.TypeKind: TTypeKind),我不是将每个都设为单个案例:case VarType(Value) and varTypeMask of varXXXX: ... end,而是寻找一种从 [= 转换的通用方法12=] 或 VariantTValue 然后与特定的 TRTTIType.

赋值兼容

在 Variant 和 RTTI 世界之间转换值的方式是什么?

此外,Spring4D 库是项目的一部分,以防有帮助。


更新:

从技术上讲,我在以下代码中寻找 Convert(在 Variant 世界中转换):

var
  Left: TRTTIField;
  Right: OleVariant;
  Temp: TValue;
  Instance: Pointer;
begin
  { Examples: varOleStr --> varXXXX --> assignment-compatible TValue }

  Right := 'False'; // varOleStr, as read with IXMLNode.GetAttributeNS
  Right := Convert(Right, Left.FieldType); // making it possibly varBoolean
  Temp := TValue.FromVariant(Right); // tkEnumeration, like Left.FieldType.TypeKind

  Right := '2'; // varOleStr, as read with IXMLNode.GetAttributeNS
  Right := Convert(Right, Left.FieldType); // making it possibly varInteger
  Temp := TValue.FromVariant(Right); // tkInteger, like Left.FieldType.TypeKind

  Right := '3.1415'; // varOleStr, as read with IXMLNode.GetAttributeNS
  Right := Convert(Right, Left.FieldType); // making it possibly varDoiuble
  Temp := TValue.FromVariant(Right); // tkFloat, like Left.FieldType.TypeKind

  Right := 'Hello!'; // varOleStr, as read with IXMLNode.GetAttributeNS
  Right := Convert(Right, Left.FieldType); // making it possibly varOleStr
  Temp := TValue.FromVariant(Right); // tkUString, like Left.FieldType.TypeKind

  { ... and an assignment: }

  Left.SetValue(Instance, Temp);
end;

我找到了 VariantChangeTypeEx,但是,我不知道如何将 Left.FieldType 关联到它以使后续代码工作。 -- 我也不介意在 RTTI 世界中进行转换,而是从 Temp := TValue.FromVariant(Right) (tkUString) 开始,然后以某种方式达到分配兼容性;所以 Temp.Kind 会变成 tkEnumeration/Boolean, tkFloat,... 由 Left.FieldType.TypeKind.

给出

如何使用 RTTI 分配变体?或者,如何将 Variant 转换为 TValue 然后赋值?

注意:如果字段类型和值类型在性质上不同,RTTIField.SetValue 将失败并返回 EInvalidCast,因为 RTTI 不会尝试更改值的性质。我这里的困难是达到分配兼容性。


更新:鉴于答案,以下代码勾勒出我的解决方案:

procedure (const Value: Pointer; const RTTIField: TRTTIField; const XMLNode: IXMLNode);
var
  Temp1: OLEVariant;
  Temp2: TValue;
begin
  Assert(XMLNode.HasAttribute(Ref, Namespace));
  Temp1 := XMLNode.GetAttributeNS(Ref, Namespace);
  Temp2 := TValue.FromVariant(Temp1);
  Temp2 := Temp2.Convert(RTTIField.FieldType.Handle{, FormatSettings}); // in Spring.TValueHelper
  RTTIField.SetValue(Value, Temp2);
end;

What is the way to transition values between the Variant and the RTTI world?

System.RTTI.TValue中使用内置的class函数转换:

myTValue := TValue.FromVariant(myVariant);

Builds a new TValue record from a Variant value.

FromVariant is a static method that can be used to build TValue records with a stored Variant value. The Value parameter contains the Variant that will be stored inside the built TValue record.

TValue 中的内置类型转换在这里对您没有帮助,因为它们只允许那些明确兼容(即可分配)的类型。从技术上讲,如果您将 Variant 存储在没有 "unpacking" 的 TValue 中,这是 FromVariant 在内部所做的,它应该能够将 Variant 转换为通常可以 cast/converted 的任何内容。但是,将持有 'True' 或 'False' 的 Variant 转换为布尔值至少存在一个问题(参见 https://quality.embarcadero.com/browse/RSP-20160

然而,由于您已经在使用 Spring4D,因此您可以使用其改进的 TValue 类型转换功能。

只需使用 Spring.pasTValueHelperConvert 方法即可。

您可以在那里传递一个 PTypeInfo(在您的代码中为 Left.FieldType.Handle)和一个可选的 TFormatSettings - 默认情况下它将使用当前语言环境。