Delphi Enums 到 Variant 作为 varInteger 而不是 varUInt32

Posted

技术标签:

【中文标题】Delphi Enums 到 Variant 作为 varInteger 而不是 varUInt32【英文标题】:Delphi Enums to Variant as varInteger instead of varUInt32 【发布时间】:2018-10-23 10:40:50 【问题描述】:

Delphi 枚举值本身定义为无符号整数 - 1、2 或 4 个字节,具体取决于 MINENUMSIZE 的设置。

我有一个案例,我们使用 Variants 在应用程序之间传输不同数据类型的数据。所有其他数据类型都可以很好地工作,但现在我们意识到其他应用程序的期望是枚举值应该是有符号整数而不是无符号整数(有些确实在验证这一点)。

有没有办法配置自动变量转换,将枚举值转换为 varInteger,而不是 varUInt32?

背景:我们正在为 OPC UA 编写库代码。 OPC UA 定义了一种称为 Variant 的类型,它在 Windows Variant 中有历史,但现在以不同的方式定义。在实践中,OPC UA 定义枚举值通过网络传输为 Int32。

OPC UA 还定义了一种称为 DataValue 的数据类型,它由 Value (as Variant)、StatusCode (UInt32) 和一些 Timestamp 字段组成。

现在,我们使用 Delphi Variants 来映射 OPC UA Variant,因为它总体上运行良好。现在唯一的主要缺陷是,当您将枚举值写入 Variant 时,它会转换为 UInt32,而 OPC UA 需要 Int32。

该库接受 Variants(在 DataValues 中),作为应用程序开发人员,您只需将枚举值分配给 DataValue,一切看起来都很好。但是,该值已转换为 UInt32,当库代码看到此 Variant 时,它不再知道它实际上对应于枚举类型的变量。

如果我们能够控制自动转换(或者实际上是枚举的本机数据类型),我们可以简单地摆脱这个问题。

如果这不可能,我们真的必须尽可能地发明转换代码,但毕竟这永远不能 100% 确定。

【问题讨论】:

你不能简单地将枚举值转换为整数吗? Integer(uvYourThingy) 嗯,是的,只是这是通用库代码,我无法完全控制开发人员将写入变体的值。 该评论对我来说没有任何实际意义。您似乎在说,“我如何才能控制变体所拥有的内容,而不能对变体所拥有的内容施加任何控制?”这就像说,“我想让开发人员将任意值写入整数值,但我想确保这些值始终为正数”。 好的,我应该多解释一下背景,让它更有意义。 所以,我可能会使用开发人员规则强制执行此操作。如果您传递值的点为时已晚,请在分配它的点强制执行此操作。不要在您的代码中使用变体,使用您自己的类型来封装变体类型并具有可以做出正确决定的 setter 方法,因为他们知道此时他们正在获得一个枚举。 【参考方案1】:

您无法区分将枚举类型分配给变量,以及将整数类型分配给变量。考虑以下代码:

uses
  System.SysUtils;

type
  TFoo = (foo, bar);

var
  V: Variant;
  enum: TFoo;
  b: Byte;

begin
  V := enum;
  V := b;
end.

编译器生成以下内容:

项目 52946989.dpr.14:V := 枚举; 00422562 B8389F4200 移动 eax,$00429f38 00422567 33D2 xor edx,edx 00422569 8A15489F4200 移动 dl,[$00429f48] 0042256F B101 移动 CL,01 美元 00422571 E8BED1FFFF 调用@VarFromInt 项目 52946989.dpr.15: V := b; 00422576 B8389F4200 移动 eax,$00429f38 0042257B 33D2 xor edx,edx 0042257D 8A15499F4200 移动 dl,[$00429f49] 00422583 B101 移动 CL,01 美元 00422585 E8AAD1FFFF 调用@VarFromInt

因此,即使您挂钩处理此类赋值 (System.Variants._VarFromInt) 的库例程,您也将不可避免地获取 Byte 值以及枚举类型值的赋值。

上面的代码假设单字节枚举类型大小,但对于 2 或 4 字节枚举类型没有太大变化。与看起来像字节分配的分配不同,它看起来分别像 WordLongWord 分配。

【讨论】:

是的,这很好地解释了它。这让我实际上希望有一个编译器指令,它可以将枚举值存储在有符号整数而不是无符号整数中。显然,目前这是不可能的,所以我只需要为未来的 Delphi 编译器版本许下愿望...... 我当然不希望 Delphi 开发人员添加这样的东西。你可以写V := Integer(enumValue) 好吧,我会 :) 您已经可以定义值是字节、字还是长字,所以我认为这是一个自然的扩展 - 如果我们不考虑超出范围的值枚举范围。或者您在幕后使用有符号值有什么危险? 您提出的功能对您以外的任何人都没有帮助,它只会帮助您,因为设计决策可能会改变。我知道做出这种改变对你来说很痛苦,但这种考虑不应该被用来驱动编译器设计。应添加新功能以满足明确的需求并提供尚不存在的功能和实用程序。在我看来,支持签名的枚举存储不符合这些标准。 我明白了。 IMO,这是一种口味问题,枚举值如何在内部存储,因为它通常不会出现在任何地方。 Ord() 也将值提供为 Integer 而不是 UInt32,因此人们甚至可能认为它存储为 Integer。唯一真正重要的情况是,如果您有超过 127 个枚举标识符和 Byte 数据类型。这只是内部编译器问题。因此,由于互操作性,控制它会很有用。我非常喜欢 Delphi,因为它确实对你可以完成的事情没有限制。现在看来我已经达到了极限……

以上是关于Delphi Enums 到 Variant 作为 varInteger 而不是 varUInt32的主要内容,如果未能解决你的问题,请参考以下文章

Delphi的又一小缺陷: TDate赋值给Variant得到varDouble而不是varDate

delphi读取excel

delphi 数据导出到word

Delphi - 为啥使用 VarArrayPut?

用Delphi将数据导入到Excel并控制Excel

[delphi]如何调用excel文档