Delphi 变体类型(Variant)

function TForm1.A(str:string):variant;
begin
 Result:=str;
end;

procedure TForm1.btn1Click(Sender: TObject);
var
i:integer;
str1:string;
begin
 i:= A('123');
 ShowMessage(IntToStr(i));
end ;

procedure TForm1.btn2Click(Sender: TObject);
var
  va1:variant;
  i:integer;
str1:string;
begin
  va1:='123';
  i:= va1;
 ShowMessage(IntToStr(i));

end;

一、变体类型常用的函数介绍:
Variant: 一种可以拥有各种数据类型; 也可以告诉目前存储的数据是什么类型(通过使用VarType函数); 可以给相同的Variant分配不同的数据类型,只要Variant包含数字值就可以执行算法; variant数组只不过是variant型的数组,不必包含同类型的数据;
1、 VarArrayOf函数:更快捷的创建一维变体数组,可以创建全异的数值数组;
function VarArrayOf(const Values: array of Variant): Variant;

    VarArrayOf是一种快速(写代码快而不是运行快)的方法生成一个一维Variant数据的方法,它接受一个Variant类型的Open Array,用这个数据生成一个一维的Variant Array of Variant(类型为Variant)。由于Delphi对Open Array类型参数的支持,使你可以用一行语句生成一个数组。不过它有两个缺陷:只能生成一维数组;只能生成元素类型是Variant的数组。使用Variant数组, 与使用标准Delphi数组类似;

例如: MyArray := VarArrayOf([‘李维’, 30, ‘60’, 60.369, ‘China’]);

2、 VarArrayCreate函数:
function VarArrayCreate(const Bounds: array of Integer; VarType: TVarType): Variant;

    Bounds: 告诉数组的上下界; VarType: 决定了数组的中存储什么类型的数据。其它的数组(如二维、或者其他类型等)只能用VarArrayCreate去创建,并用循环给元素赋值。

例如:创建数组的数组, 可以模仿任何类型的数据结构类型:
VarArrayX := VarArrayCreate([1,10], varVariant);
数组的单个元素可以装载一个数组: VarArrayX[1] := VarArrayCreate([1,5], varVariant);

3、VarArrayHighBound、VarArrayLowBound函数:返回变体数组上/下限,从1开始(1:列;2:行;3:3维的最后一维)。
function VarArrayHighBound(const A: Variant; Dim: Integer): Integer;

4、VarArrayRedim函数:修改variant数组的最高限。
procedure VarArrayRedim(A: Variant; HighBound: Integer);

5、VarArrayDimCount函数:返回variant数组维数
unction VarArrayDimCount(const A: Variant): Integer;

6、VarArrayLock、VarArrayUnLock函数:避免运行时刻检查。一般用于初始化一个大数组。
function VarArrayLock(const A: Variant): Pointer;
procedure VarArrayUnlock(var A: Variant);

    在对Variant数组的每一个元素进行赋值时,都要通过运行时逻辑来检查并判断数据类型的兼容性、每个元素的位置等等。为了避免运行时检查,要用VarArrayLock()函数和VarArrayUnlock()过程。VarArrayLock()函数在内存中锁定数组,使数组不再移动和改变大小,并能返回一个指向数组数据的指针。而VarArrayUnlock()过程用来对VarArrayLock()函数锁定的数组进行解锁,使数组能重新移动或改变大小。在锁定数组后,能用更有效的方法对数组进行初始化。

7、VarArrayRef函数:获得variant指向的数据
function VarArrayRef(const A: Variant): Variant;

8、VarIsArray函数:是一个简单的布尔检查函数,判断是否是一个 Variant数组,是则返回True
function VarIsArray(const A: Variant): Boolean; overload;
function VarIsArray(const A: Variant; AResolveByRef: Boolean): Boolean; overload;

9、其他函数

1>、VarClear()过程清除Variant变量并将VType域的值设为varEmpty.
2>、VarCopy()将Source复制到Dest。
3>、VarCast()将一个Variant转换成指定的类型并存储在另一个Variant变量中。
4>、VarType()返回指定Variant的varXXX类型代码。
5>、VarAsType()跟VarCast()的功能一样。
6>、VarIsEmpty()如果一个Variant变量的类型代码是varEmpty则返回True。
7>、VarIsNull()判断Variant变量是否包含null值。
8>、VarToStr()将一个Variant变量转换成字符串表达式(如果Variant为varEmpty或varNUll则为空字符串)。
9>、VarFromDateTime()返回一个Variant变量,它存放着指定的TDateTime类型的值。
10>、VarToDateTime()返回在Variant中的TDateTime类型的值。

二、一个简单实例代码:
var

a, b: Variant;

I, J, K, M: Integer;

begin

//a是个一维Variant数组,元素类型是Variant,元素个数是3,上界是2,下界是0。

a := VarArrayOf([1234, 'abc ', Null]);

//b是一个16行4列的二维数组

b := VarArrayCreate[0, 3, 1, 16], varOleStr);

I := VarArrayHighBound(b, 1); // I是b的最大列号:3;

J := VarArrayLowBound(b, 1); // J是b的最小列号:0;

K := VarArrayHighBound(b, 2); // K是b的最大行号:16;

M := VarArrayLowBound(b, 2); // M是b的最小行号:1。

end;

注意,Variant数组下标是列在前,行在后,元素类型是PWideChar,最大列号是3,最小列号是0,最大行号是16,最小行号是1。

三、变体类型和流的相互转换
//1、 变体类型转成流

procedure VariantToStream(const Data: OleVariant; Stream: TStream);

var

p: Pointer;

begin

p := VarArrayLock(Data); // 加锁,并返回一个指针指向的数据。 避免运行时时刻检查

try

Stream.Write(p^, VarArrayHighBound(Data,1) + 1);  // 返回最大的列号

finally

VarArrayUnlock(Data); // 解锁,使数组能重新移动或改变大小。

end;

end;

// 2、流转成变体类型

function StreamToVariant(Stream: TStream): OleVariant;

var

p: Pointer;

begin

Result := VarArrayCreate([0, Stream.Size - 1], varByte);// 创建一个以为的数组。类型为varByte

p := VarArrayLock(Result);

try

Stream.Position := 0;  // 设置流的位置

Stream.Read(p^, Stream.Size);

finally

VarArrayUnlock(Result);

end;

end;

delphi 为了完全支持OLE,32位Delphi 增加了Variant 数据类型,本节将从宏观角度来分析这种数据类型。实际上,Variant类型对Pascal语言有普遍而深入的影响,Delphi 控件库中与OLE 无关的地方也使用到这种类型Variant变量没有类型 一般说来,你可以用Variant 变量存储任何数据类型,对它执行各种操作和类型转换。需要注意的是:这违反了Pascal 语言的一贯原则,有悖于良好的编程习惯。variant 变量的类型检查和计算在运行期间才进行,编译器不会提示代码中的潜在错误,这些错误在进一步测试中才能发现。总之,你可以认为包含variant变量的代码是解释性代码,正如解释性代码一样,许多操作直到执行时才能知道,这对代码运行速度会有很大的影响。 上面对Variant 类型的使用提出了警告,现在来看看Variant 类型究竟能干什么。基本上说,如果声明了一个variant 变量: var V: Variant; 你就可以把各种不同类型的值赋给它: V := 10; V := 'Hello, World'; V := 45.55; 一旦得到一个variant 值,你可以把它拷贝给任何兼容或不兼容的数据类型。如果你把值赋给不兼容的数据类型Delphi 会力尽所能进行转换,无法转换则颁布一个运行时间错误。实际上,variant变量中不仅包含了数据还包含有类型信息,并允许一系列运行时间操作,这些操作很方便,但运行速度慢且安全性差。 见例VariTest,它是上面代码的扩展。窗体上有三个编辑框,一对按钮,第一个按钮的OnClick 事件代码如下: procedure TForm1.Button1Click(Sender: TObject); var V: Variant; begin V := 10; Edit1.Text := V; V := 'Hello, World'; Edit2.Text := V; V := 45.55; Edit3.Text := V; end; 很有趣是不是?你可以把一个值为字符串的variant 变量赋给编辑框Text 属性,还可以把值为整数或浮点数的variant 变量赋给Text属性。正如你在图10.1中所看到的,一切正常。 (图10.1)按Assign按钮后,例VariTest的输出结果 图 10.1: 例 VariTest 的 Assign 按钮 Click 事件输出结果 更糟糕的是:你还可以用variant变量计算数值,从第二个按钮的Click事件代码就可看到这一点: procedure TForm1.Button2Click(Sender: TObject); var V: Variant; N: Integer; begin V := Edit1.Text; N := Integer(V) * 2; V := N; Edit1.Text := V; end; 至少这种代码带有一定危险性,如果第一个编辑框包含了一个数字,那么一切运行正常;如果不是,将会引发异常。这里再重申一遍,如果不到万不得以,不要随便使用Variant 类型,还是应坚持使用传统的Pascal 数据类型类型检查方法。在Delphi 和 VCL中,variant变量主要是用于 OLE 支持和数据库域的访问。 Variant类型内部结构 Delphi中定义了一个 variant 记录类型,TVarData,它与Variant 类型有相同的内存布局。你可以通过TVarData访问variant变量的实际类型。TVarData 结构中包含了Variant类型信息(由Vtype域表示)、一些保留域及当前值。 VType域的取值包括OLE 自动化中的所有数据类型,这些类型通常叫OLE 类型variant 类型。以下是variant 类型的完整列表,按字母顺序排列: varArray varBoolean varByRef varCurrency varDate varDispatch varDouble varEmpty varError varInteger varNull varOleStr varSingle varSmallint varString varTypeMask varUnknown varVariant 你可以在Delphi 帮助系统的variants 主题下找到这些类型的说明。 还有许多操作variant 变量的函数,你可以用它们进行特定的类型转换,或通过它们获取variant变量的类型信息(例如VarType 函数),当你用variant变量写表达式时,Delphi会自动调用这些类型转换和赋值函数。另外还有操作variant 数组的例程,你可以通过帮助文件的Variant support routines 主题了解相关内容。 Variant类型运行很慢! Variant 类型代码运行很慢,不仅数据类型转换如此,两个值为整数的Variant 变量相加也是如此。它们几乎跟Visual Basic这种解释性代码一样慢!为了比较Variant变量和整型变量的运行速度,请看例VSpeed 。 程序中设置了一个循环,记录运行时间并在进程条中显示运行状态。下面是基于variant类型的一段代码,基于整型的代码与此相似: procedure TForm1.Button1Click(Sender: TObject); var time1, time2: TDateTime; n1, n2: Variant; begin time1 := Now; n1 := 0; n2 := 0; ProgressBar1.Position := 0; while n1 < 5000000 do begin n2 := n2 + n1; Inc (n1); if (n1 mod 50000) = 0 then begin ProgressBar1.Position := n1 div 50000; Application.ProcessMessages; end; end; // we must use the result Total := n2; time2 := Now; Label1.Caption := FormatDateTime ( 'n:ss', Time2-Time1) + ' seconds'; end; 记时这段代码值得一看,因为你可以把它用到任何类型的性能测试中。正如你所看到的,程序用Now 函数获取当前的时间,用FormatDateTime 函数格式化时间差,输出结果以分("n")和秒("ss")表示。除此之外,你可以用Windows API的GetTickCount 函数,该函数能精确显示操作系统启动后至当前的毫秒数。 从上例可见两者的速度差异非常之大,以至于不用精确记时也能看到这种差异。图10.2是在本人计算机上运行程序看到的结果。当然运行结果取决于运行程序的计算机,但是两者的数值比不会有太大变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值