记录

记录

记录的定义

记录相当于 C 语言的结构体,定义例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type 
TMyDateRecord = record
year: Integer;
month: Integer;
day: Integer;
end;
//
var myBirthDay : TMyDateRecord; // 这个记录存在于局部变量的栈空间
//
begin
myBirthDay.year := 2002;
myBirthDay.month := 3;
myBirthDay.day := 9;
end;

记录赋值

  1. 记录赋值,可以各个成员单独逐个进行赋值
  2. 记录类型的变量可以直接赋值给另一个相同类型的记录
1
2
3
var myBirthDay1, myBirthDay2 : TMyDateRecord;

myBirthDay1 := myBirthDay2;

变体记录

变体记录的作用相当于 C 语言的 union,可以多个字段共用一段重复的内存空间,避免浪费。

变体记录的声明格式是:

1
2
3
4
5
6
7
8
9
10
11
type
记录类型名 = record
字段1 : 类型1;
字段2 : 类型2;
...
case [tag:] 有序类型 of
常量1: ( 字段声明列表1 );
常量2: ( 字段声明列表2 );
常量3: ( 字段声明列表3 );
...
end;

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type 
TMyRecord1 = record
value1: Integer;
value2: Integer;
case Integer of
1: (value3: Integer);
2: (
value4: Integer;
value5: Integer;
);
3, 4: (value5: Double)
end;
//
var vrecord : TMyRecord1;
begin
vrecord.value5 := 8; // 可以直接访问变体内的字段
end.
  • case 前为记录的固定部分
  • case 后的部分为变体部分,一直到 end 结束
  • 变体部分的大小由里面占用空间最大的部分决定
  • 变体部分的每个 case 常量值占用的空间是共用的
  • 最后一次 case 常量值的声明的部分的分号可以省略
  • case 没有对应的 end,最后的 end 是跟 record 对应的
  • tag 可选声明,如果有声明的话,可以作为记录的一个字段
  • 有声明 tag 时,记录中 tag 会多占用一个 tag 类型的空间
  • 变体中,不可以存在长字符串、动态数组、变体、接口等类型或衍生类型

SizeOf

SizeOf 内置函数可以获得记录占用的大小。可以传递记录的类型名,也可以传递记录的变量名

packed record

packed record 一共成为压缩记录,它会忽略字段对齐,把字段压缩成最小的记录。

1
2
3
4
5
6
type
记录类型名 = packed record
字段1 : 类型1;
字段2 : 类型2;
....
end;

记录的封装

可以声明 private 和 public 部分,将记录封装。

不同于类:

  • private 部分内的字段,在本 unit 单元内可以访问,外部单元则不可访问。
  • public 部分内的字段,在本 unit 单元和外部单元都可以访问。
1
2
3
4
5
6
7
8
9
10
11
type 
TMyRecord = record
private
inner1: Integer;
inner2: Integer;
inner3: Integer;
public
pub1: Integer;
pub2: Integer;
pub3: Integer;
end;

带方法的记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type 
TMyRecord = record
private
inner1: Integer;
inner2: Integer;
inner3: Integer;
public
procedure SetInner1(v: Integer);
function GetInner1: Integer;
end;

procedure TMyRecord.SetInner1(v: Integer);
begin
inner1 := v;
end;

function TMyRecord.GetInner1: Integer;
begin
Result := inner1
end;

Self

Self 是一个隐藏的记录引用。调用记录中的方法,会隐藏传递一个本记录实例的引用,叫做 Self

构造器

构造器用于记录初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type 
TMyRecord = record
private
inner1: Integer;
public
constructor Create (newValue: Integer); // 声明构造器
end;

constructor TMyRecord.Create (newValue: Integer); // 实现构造器
begin
inner1 := newValue;
end;

var rec1, rec2 : TMyRecord;

rec1 := TMyRecord.Create(100); // 初始化方式 1
rec2.Create(200); // 初始化方式 2

记录的构造器必须带有参数,不允许不带参数,否则编译错误。

托管记录

记录的初始化还有另外一种方式:托管记录。

当记录只具有普通(非托管)字段时,编译器不需要做太多操作。

当记录中包含了长字符串、接口、动态数组、变体等托管类型时,它就是托管记录。编译器会在托管记录使用的代码周围添加一个 try-finally 块,以确保即使出现异常也能清除数据。编译器会为托管记录执行默认的 Initialize 初始化和 Finalize 终止化的操作。

10.4 版本后,允许用户自定义 InitializeFinalize 操作,通过运算符重载的方式来进行自定义:

1
2
3
4
5
6
type
TMyRecord = record
Value: Integer;
class operator Initialize(out Dest: TMyRecord);
class operator Finalize(var Dest: TMyRecord);
end;

加入 Initialize 和 Finalize 操作符重载的记录,会被编译器系统识别为托管记录;编译器会在使用记录中自动加入 try-finally