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 Integer of ,integer 怎么充当变量了呢,也许不是变量,但到底是什么意思,请大家给我一个详细的解答,让我补一课
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 Integer of ,integer 怎么充当变量了呢,也许不是变量,但到底是什么意思,请大家给我一个详细的解答,让我补一课
case boolean of
false:(a:byte);
true :(b:Char);
end;
procedure TForm1.FormCreate(Sender: TObject);
var tt:Test;
begin
tt.a:=97;
showmessage(tt.b); tt.b:='b';
showmessage(inttostr(tt.a));
end;看这个例子你就能理解了
case boolean of
false:(a:byte);
true :(b:Char);
end;
也可以写成,真正的变量被隐藏了,你看到的integer只是那个变量的类型
type test=record
case jinjazz:boolean of
false:(a:byte);
true :(b:Char);
end;
type test=record
case boolean of
false:(a:byte);
true :(b:Char);
end;
在记录类型中域“A”,“B”其实是使用的一个内存空间来存储数据的,这个共用的内存空间可以存储
BYTE,CHAR两种类型的数据,但是在何时存储BYTE?何时存储CHAR呢?因此在变体记录中存在一个标志位,
就是BOOLEAN了。^_^,至于BOOLEAN的值你不用烦啦。BORLAND替你把判断做好啦!
这么做是使你的自动义数据类型能够兼容多种数据类型但同时又能节约内存空间。
主要解答者: plainsong 提交人: FrameSniper
感谢: lxpbuaa、FrameSniper、jacky_shen
审核者: hthunter 社区对应贴子: 查看 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;
上面这个记录类型里的Integer是从哪里来的?
想想总觉得有些奇怪,对于一个记录类型来说,它的字段是可以为任意数据类型的,对记录类型的数据进行操作都是通过这样来进行的:MyRecord.Field,好像不能传递参数,那么这个Integer在那里呢,是在对记录进行操作的函数或过程内吗?
---------------------------------------------------------------
以下文字摘自拙作《Delphi精要》:
一个变体记录典型的定义如下:
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位。
------------------------------------
宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
------------------------------------
---------------------------------------------------------------
天啊,都这么多人,说的很详细了,不过一楼的一个地方说的不对
记录的变体部分的条件域必须是有序类型,而不是什么可以计算大小的类型,另外这句话表达不对----“如果你给constantList1赋值,那么constantList2-constantListn也就被赋了值”,记录的变体部分的实际类型是Variant类型,所以这里可以定义成任何类型,同时空间大小服从所有变体类型中占用空间最大的那个,任何时刻变体部分的类型都是唯一的,其他类型此时没有意义,也不存在,所以不能说一个被赋值,所有的全部赋予了值!
---------------------------------------------------------------
一不小心提交了。继续……
Long String、WideString、Dynamic Array、Interface的大小都是指针大小,OleVariant其实就是COM SDK中的VARIANT结构,大小是16字节。但在Object Pascal中它们都需要自动终结化,如果它们出现在variant part中,编译器就无法知道它们是否应该进行终结化--因为不知道当前存储的是哪种类型。
如果学过C语言,对variant part应该很好理解,它就相当于union。只是多了些限制:每个记录体只能有一个variant part;只能出现在记录体的结尾;不允许出现自动终结化的类型。
---------------------------------------------------------------
记录中的变体部分
记录类型中可以含有变体部分,有点象case语句。变体部分必需在记录中其他字段的声明之后。
声明含有变体部分的记录类型,语法如下:
type recordTypeName = record
fieldList1: type1;
...
fieldListn: typen;
case tag: ordinalType of
constantList1: (variant1);
...
constantListn: (variantn);
end;
在保留字case之前的声明部分与标准记录类型中的声明相同。剩余从case到end之前最后一个可选的分号(;)之间叫做变体部分。在变体部分中:
· tag是可选的,并且可以是任意有效的标识符。如果忽略tag,那么也要同时忽略紧随其后的冒号(:)。
· ordinalType表示一个序数类型。
· 每个constantList都是一个表示ordinalType类型的常量,或者逗号隔开的此类常量列表。在当前变体部分声明中,对于所有的constantLists,同一个值最多只能出现一次。
· 每个variant都是一个逗号隔开的声明列表,这与记录类型主要部分中的fieldList: type结构相似。也就是说,variant具有如下形式
fieldList1: type1;
...
fieldListn: typen;
这里,每个fieldList是一个有效标识符或者逗号隔开的标识符列表,每个type代表一个类型,最后的分号是可选的。所有的类型(types)都不能是长串、动态数组、变体(即Variant类型)或接口,也不能是包含长串、动态数组、变体(即Variant类型)或接口的结构类型;但可以是指向这些类型的指针。
含有变体部分的记录在语句构成上比较复杂,但在语义上比较简单,具有欺骗性。记录中的变体部分包含几个变体,在内存中它们共享相同的空间。可以在任何时候对记录中任何变体的任何字段读或写;但如果在一个变体中写一个字段并且在另一个变体中写另一个字段,那么后面的写操作可能覆盖前面写入的数据。对于Tag,如果存在,那么将作为记录中的非变体部分,被视为额外的字段(其类型是ordinalType)。
变体部分有两种用途。首先,假设要创建一个记录类型,它有不同数据种类的字段,但不需要在单个记录实例中使用全部数据。例如,
type
TEmployee = record
FirstName, LastName: string[40];
BirthDate: TDate;
case Salaried: Boolean of
True: (AnnualSalary: Currency);
False: (HourlyWage: Currency);
end;
这里的意思是,每个雇员都有年薪或计时工资,但不能都有。因此,在创建TEmployee时,没有理由为两个字段都分配足够的内存。这种情况下,变体之间只有名字不同,而要想让字段的类型不同也是很容易的。考虑相对复杂的例子:
type
TPerson = record
FirstName, LastName: string[40];
BirthDate: TDate;
case Citizen: Boolean of
True: (Birthplace: string[40]);
False: (Country: string[20];
EntryPort: string[20];
EntryDate, ExitDate: TDate);
end;
type
TShapeList = (Rectangle, Triangle, Circle, Ellipse, Other);
TFigure = record
case TShapeList of
Rectangle: (Height, Width: Real);
Triangle: (Side1, Side2, Angle: Real);
Circle: (Radius: Real);
Ellipse, Other: ();
end;
对上面的每个记录实例,编译器分配足够的内存以在最大的变体中能够保存所有的字段。可选的Tag和常量列表constantLists(如上面最后一个例子中Rectangle、Triangle等)在编译器管理字段的途径中不扮演任何角色,它们仅为程序员提供方便。
变体部分的第二个用途是,可以将相同的数据视为不同的类型,尤其在编译器不允许类型转换的情况。例如,如果有一个64位的实数Real类型作为变体的第一个字段,一个32位的整数Integer类型作为另一个变体的第一个字段,那么可以向实数Real字段赋值然后以整数Integer字段读出其前32位(简单地说,作用就是请求整数参量)。