用户登录  |  用户注册
首 页商业源码原创产品编程论坛
当前位置:PB创新网文章中心编程技巧Delphi

强数据类型与类型转化

减小字体 增大字体 作者:佚名  来源:本站整理  发布时间:2009-03-16 20:04:26
强数据类型与类型转化
Object Pascal是一种“强数据类型”语言。

所谓“强数据类型”,就是严格区分不同数据类型,并不总是允许不同类型数据直接赋值。例如:

var

B: Boolean;

begin

B := 1;

end;



上面的代码期望将整数直接赋值给一个布尔变量,这在Object Pascal中是行不通的。编译上面的程序时,编译器会生成如下错误信息:

[Error] Unit1.pas(29): Incompatible types: ''Boolean'' and ''Integer''

上述信息表示Boolean和Integer类型不兼容。

但是大家知道在C等语言中,是可以将一个整型数赋值给一个布尔变量的。

Pascal以语法严谨而著称,强类型是其严谨的一个重要体现。但是强类型特性也不是为了体现严谨而刻意加上去的,如果那么搞的话,恐怕今天也不会有这么多人使用Delphi了。强类型是有好处的,那就是:可以避免运行时不同类型数据转化出现错误,因为在程序被编译时,潜在的错误就已经被消灭了。

如果说世界上有绝对完美的东西,大家都是不相信的,就像无法找到满足聪明、漂亮、脾气好等100个条件的女朋友那样,那只能是做梦。强数据类型尽管有它的好处,但是在不同数据类型相互转化时则带来了很大的障碍,显得非常死板。而数据类型转化对于一个程序员来说,就像肉食对我那样重要,每天都不可缺少。为此,Object Pascal又特意提供了一些数据类型转化方法。归纳起来,有以下一些:

typecasting, pointers, variants, variant parts in records, absolute addressing

1. typecasting(类型强制转化)

顾名思义,typecasting是将一个类型的数据强制转化为另一个类型的数据。其典型语法如下:

typeIdentifier(expression)

其中typeIdentifier是结果类型标识符,expression可以是原始数据表达式或者变量。如:



var

B: Boolean;

begin

B := Boolean (1); {将整数1强制转化为布尔类型,结果B=True}

end;



对于对象和接口,除了使用typeIdentifier外,Object Pascal还专门提供了as操作符进行转化。在使用as前应该首先判断源对象和结果类型是否兼容、源对象/源接口是否支持结果接口,可分别使用is操作符和Supports函数来作这种判断。

2. pointers(指针)

首先定义一个结果类型的指针,然后将该指针指向原始表达式(使用操作符@或者函数Addr),然后从指针所指地址中取出结果类型的数据(使用操作符^)。如:



var

I: Integer;

P: PBoolean;

B: Boolean;

begin

I := 1;

P := @I;

B := P^; {此时B=True}

end;



3. Variants(可变类型)

Variant可以存储任何类型的表达式,因此,通过Variant这样一个中转站,可以实现数据类型转化。如:



var

V: Variant;

B: Boolean;

begin

V := 1;

B := V; {通过Variant实现类型转化,此时B=True}

end;



4. variant parts in records(变体记录)

一个变体记录典型的定义如下:



type recordTypeName = record

fieldList1: type1;

……

fieldListn: typen;

case tag: ordinalType of

constantList1: (variant1);

……

constantListn: (variantn);

end;

其中case到结尾部分定义了多个变体字段。所有变体字段共享一段内存,换句话说,如果你给constantList1赋值,那么constantList2-constantListn也就被赋了值。至于这些变体字段返回什么值,则是由它们的类型决定。程序根据tag的值决定应该使用constantList1-constantListn中的哪个字段。例如:



type

TDataConv = record

case T: Boolean of

True: (I: Byte);

False: (B: Boolean;);

end;



var

D: TDataConv;

begin

D.I := 1; {此时D.B=True,因为I和B这两个变体字段共享一段内存}

end;



使用变体记录时要注意:

(1)变体字段不能是long strings、dynamic arrays、variants、interfaces等由编译器自动管理内存的类型,也不能是含有上述类型的构造类型,但可以是这些类型和构造类型的指针。

(2)所有变体字段共享一段内存。而共享内存的大小则由最大变体字段决定。

(3)当tag存在时,它也是记录的一个字段。也可以没有tag。

再看Messages单元定义的消息类型TMessage:



TMessage = packed record

Msg: Cardinal;

case Integer of

0: (

WParam: Longint;

LParam: Longint;

Result: Longint);

1: (

WParamLo: Word;

WParamHi: Word;

LParamLo: Word;

LParamHi: Word;

ResultLo: Word;

ResultHi: Word);

end;

case部分并没有tag字段,接下来的0和1只是为了给变体字段分组,0部分的三个字段和1部分的六个字段共享一段内存。这段内存大小是4(Longint即Integer,占用4个字节)´3=12个字节。一个Word占用2个字节。我们知道一个32位整数在内存中是高字节在后,低字节在前,因此,WParamLo被对应到WParam的低16位,WParamHi被对应到WParam的高16位,依此类推。换句话说,通过WParamLo可以取得WParam的低16位,而通过WParamHi可以取得WParam的高16位。

absolute addressing(绝对地址)

声明共享一段内存的多个变量,从而实现类型转化。和变体字段的机理相似。

一般都很少使用绝对地址来实现数据共享和类型转化,因为它在声明时就固定了变量的地址,导致灵活性缺乏。

下面一段代码演示了如何使用绝对地址来共享数据:



var

I: Integer;

B: Boolean absolute I; {其中absolute是共享关键字,它让B和I共享一段内存}

begin

I := 1; {此时B=True}

end;



上面所讲的各种类型转化方法,本质上都是通过数据的内存地址操作来实现的,因此,理解这些转化方法的关键还是在于理解地址、指针这些概念。

除了上面列出的几种转化方法以外,Delphi也提供了大量的函数和方法来直接实现特定的数据类型转化。关于这些函数和过程的详细列表和功能介绍,请参看“8.1 数据类型转化类”。

在本小节最后,我想举一个较全面的例子,来加深我们对类型转化的认识。这个例子是说非整数类型数据如何在消息中传递。

对于以下两个常用的发送消息API函数:



function PostMessage(hWnd: HWND; Msg: UINT;

wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;

function SendMessage(hWnd: HWND; Msg: UINT;

wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;



我们知道其中各个参数含义如下:

hWnd:接受该消息的窗口的句柄;

Msg:消息代号;

wParam、lParam:第一个、第二个消息数据。其类型WPARAM = LongInt = Integer。

如果我们需要通过wParam、lParam来传递非整数类型的数据,如一个记录、字符串甚至对象时,应该怎么办呢?这两个参数只能是Integer类型啊!

很显然,这时候我们要将非整数类型的数据转化为一个整数,然后才能作为参数传递。消息接收方收到消息,再将整数类型的消息参数还原为原始类型。

怎么实现这种数据类型转化呢?那就是传地址。即使用消息参数传递数据变量的地址,而地址是用整数表示的。

比如Windows标准消息WM_GETTEXT用于取得一个窗口的文本,下面我们就利用它来取得一个Edit的Text属性。

WM_GETTEXT需要参数lParam作为存储返回结果的缓冲区,wParam指定要求取得数据的最大长度。于是我们可以写出如下代码:



var

PText: PChar;

begin

GetMem(PText, MAXBYTE);

{注意下面一行的Integer(PText),它取得变量PText的地址作为参数传送}

SendMessage(Edit1.Handle, WM_GETTEXT, MAXBYTE, Integer(PText));

ShowMessage(PText);

FreeMem(PText);

end;



接收消息时需要通过地址取得原始数据。下面我们利用消息WM_COPYDATA举个完整例子:

发送消息:



procedure TForm1.Button2Click(Sender: TObject);

var

Data: CopyDataStruct;

MsgText: PChar;

begin

MsgText := ''lxpbuaa'';

with Data do

begin

dwData := 100;

lpData := MsgText;

cbData := Length(MsgText);

end;

SendMessage(Handle, WM_COPYDATA, Handle, Integer(@Data));

end;

接收消息:



{首先声明一个消息方法映射消息:}

procedure WMCopydata(var Msg: TMessage); message WM_COPYDATA;



{实现该消息方法:}

procedure TForm1.WMCopydata(var Msg: TMessage);

var

Data: CopyDataStruct;

S: String;

begin

{因为传过来是数据的地址,所以我们首先用CopyDataStruct的指针类型PcopyDataStruct
将地址转化为一个指针,再用^操作符取得指针所指实际内容}

Data := PCopyDataStruct(Msg.LParam)^;

with Data do

begin

S := ''数据类别代码:'' + IntToStr(dwData) + #13 +

''消息实际数据:'' + PChar(lpData);

end;

ShowMessage(S);

end;

小结
本小节描述了Pascal强类型特性的优点和缺点,并全面介绍了克服该缺点的几种方法。熟练掌握这些方法,可以大大提升在编写程序时调度各种数据类型的能力。

Tags:

作者:佚名

文章评论评论内容只代表网友观点,与本站立场无关!

   评论摘要(共 0 条,得分 0 分,平均 0 分) 查看完整评论
PB创新网ourmis.com】Copyright © 2000-2009 . All Rights Reserved .
页面执行时间:28,968.75000 毫秒
Email:ourmis@126.com QQ:2322888 蜀ICP备05006790号