此外,
每个单元有两个不同的部分。接口和实现。
接口部分包含所有公共定义(类型、过程标题、常量)。实施部分包含所有实施细节。
当您使用一个单元时,(使用 uses 子句)您可以访问该单元的公共定义。这种访问不是递归的,因此如果单元 A 接口使用单元 B,而单元 C 使用单元 A,除非您明确使用它,否则您无法访问单元 B。
实现部分可以访问接口,可以访问两个使用子句(接口和实现)中使用的单元。
在继续编译其余单元之前,首先编译已使用单元的接口。这样做的好处是您可以从实现中获得循环依赖:
unit A;
interface
uses B;
unit B;
interface
implementation
uses A;
编译:
- 尝试接口A,失败需要B
- 试试B接口,ok!
- 试试接口A,ok!
- 尝试实现A,好的!
- 尝试实施B,好的!
每个单元也有一个初始化部分(如果它有一个初始化部分,它也可以有一个终结部分。)初始化部分用于初始化单元的变量。终结部分用于清理。当您使用这些时,明智的做法是不要指望其他单元的初始化。只要让它们简单而简短。
单位也是命名空间。考虑以下问题:
unit A;
interface
const foo = 1;
unit B;
interface
const foo = 2;
unit C;
interface
uses A, B;
const
f1 = foo;
f2 = A.foo;
f3 = B.foo;
如果在多个使用的单位中定义了一个标识符,则采用使用列表中可能的最后一个单位。所以 f1 = 2。但是你可以在它前面加上单元(命名空间)名称来解决这个问题。
随着 .net 的引入,允许使用多部分命名空间,这引入了其他一些不错的问题:
unit foo;
interface
type
rec1 = record
baz : Boolean;
end;
var
bar : rec1;
unit foo.bar;
interface
var
baz : Integer;
uses
foo, foo.bar;
begin
foo.bar.baz := true;
foo.bar.baz := 1;
end.
// 1. Which these lines gives an error and why?
// 2. Does the result change if you write uses foo.bar, foo?
在这种情况下,您有冲突。但这可以通过赋予命名空间名称更高的优先级来解决。所以第一行失败了。