当前位置:首页>java>Delphi 编程 -- 谈谈变体类型

Delphi 编程 -- 谈谈变体类型

  • 2026-08-27 03:24:37
Delphi 编程 -- 谈谈变体类型
Delphi 中的变体(Variant)类型不同于我们前面讨论过的变体记录类型,变体类型是一个系统类型,而变体记录类型则是一个用户定义类型,尽管二者在某些实现方面具有一致性。
变体类型主要用于两个方面:
  • 类型可变或编译时刻无法确定类型的情况,此类可称之为“动态类型”;
  • 需要额外语义来描述其值为空的情况,此类可称之为“可空值类型”。
可空值类型主要用于与数据库的接口,这个我们以后再谈,今天主要讨论动态类型。
先看一个例子:
program VTest1;{$APPTYPE CONSOLE}var  v: Variant;begin  v := 'Hello, Variant!';  // 此时,v 是 UnicodeString 类型  Writeln(v);  v := 10;      // 此时,v 是 Integer 类型  Writeln(v);  v := 3.4;     // 此时,v 是 Currency 类型  Writeln(v);end.
这段代码展示了变体类型的强大威力:犹如变色龙一般,可以随意变换“色彩”。你可能会是说,这样一身而多任,不好么?
答案是:不好!
原因可以追溯到遥远的18世纪甚至更早,当苏格兰经济学家亚当斯密在其出版的《国富论》中提出“劳动分工”理论时,就已经说明“专业的人做专业的事”,比“一个人做所有的事”,效率要高得多。
回到我们的例子。变体类型不仅占用空间大(变体类型是一个很复杂的类型,一个变体类型的变量在32位时需占用 16 字节,在64位时需占用24字节),而且效率低、速度慢。
下面具体说明我们的结论。
首先来看变体类型的内部表示。
Delphi 的变体类型由 TVarData 实现,TVarData 与 Windows 中的 OLE 实现的 Variant 类型是一致的。TVarData 记录的定义如下:
从 TVarData 的类型定义可以看出,其中的 RawData 字段定义了 TVarData 记录的长度,32位时,RawData 是 array[0..3] of Integer,也就是 4 个元素的整型数组,占用 16 个字节。64位时,RawData 是 array of [0..5] of Integer,也就是 6 个元素的整型数组,占用 24 个字节。
VType 字段表示当前的数据是什么类型,下面的 case 语句定义了一系列的 varXXX 类型,这些就是 Delphi 的变体类型所支持的具体类型。譬如,varSmallInt 表示 16 位整型,varInteger 表示 32 位整型,varSingle 表示 32 位单精度浮点型,varDouble 表示 64 位双精度浮点型,等等。对于这些简单类型来说,变体类型将保存这些类型的值,如对于开头的例子,当对变体类型 v 赋值 10 时,v 中保存的是 10 这个值本身,3.4 也是一样。但对于字符串,v 中保存的是指向字符串的指针,这可以从上面类型定义中的 VarString: (VString: Pointer); 这一行看出来。
前面我们说变体类型是一个很复杂的类型,看过 TVarData 的类型定义之后,就知道所言不虚了。
下面我们看看 Delphi 如何实现变体类型的赋值语句:
这是 Delphi 为开头的例子生成的代码,可以看到,每一个赋值语句,不论是将字符串赋值给变体类型变量 v,还是将整型数或者浮点型数赋值给 v,都是通过调用 $VarFromXXX 过程实现的。下面以其中的 VarFromCurr 过程为例,来看看变体类型的赋值过程:
procedure _VarFromCurr(var V: TVarData; const Value: Currency);begin  if (V.VType and varDeepData) <> 0 then    VarCleanDeep(V);  V.VType := varCurrency;  V.VCurrency := Value;end;
首先检查 V 是否含有字符串、数组以及其他需要进一步清理的类型,如果是,则调用 VarCleanDeep 过程对 V 进行进一步的深度清理。
接下来才是具体的赋值:对 VType 字段赋予 varCurrency 类型,同时将 Currency 类型值赋予 VCurrency 字段。
以上这只是像 Currency 这样的简单类型的赋值,如果像数组、字符串等更复杂类型的赋值,所需要的工作更多。
以上只是简单的赋值语句,其他对变体类型的操作也同样需要调用特定的过程来实现。变体类型不仅结构复杂,而且操作同样复杂,这就大大降低了操作变体类型数据的性能。同时,因为变体类型绕过了编译时刻的类型检查,许多类型错误会导致抛出运行时异常。
既然变体类型这么复杂,为什么还要用变体类型呢?
如果所有的操作都能在 Delphi 内完成,确实不需要变体类型。变体类型主要用于与外部世界的接口,譬如 Windows 平台的 COM,譬如数据库系统。

最新文章

随机文章