在 Delphi 中,一个正在开发的应用程序可以被称作项目或者工程。一般地,一个项目主要由 dpr (项目)、 pas (单元)和 dfm (窗体)三种文件组成,另外还有一些附属文件,如 res (资源)文件等。其中项目文件可以被看做是一种特殊的单元。在源代码中,项目文件用关键字 program 标识,单元文件用 unit 标识。
通常,一个项目只有惟一的 dpr 文件。一个 dfm 文件总是有对应的 pas 文件,但是 pas 文件可以没有对应的 pas 文件。
如果打开 Delphi ,选择菜单 File|New|Application ,则可以新建一个项目。该项目包括一个项目文件— — Project1.dpr 、一个窗体文件— — Unit1.dfm 和一个对应的单元文件— — Unit1.pas 。选择菜单 Project|View Source 可以看到项目文件的内容;在窗体上单击右键,从弹出的菜单中选择 View as Text 可以看到窗体文件的内容。
这些文件的内容都必须按照一定的组织结构来编写,编译器按照既定的组织结构来识别这些内容并进行编译。在本节里,我们讨论 program 和 unit 两种单元文件的组织结构。
Program 的组织结构
一个项目文件被关键字 program 标识,因此,在这里我将项目文件的源代码称作 program 。以下是一个简单的 program :
program Project1; { 文件定义 : 一个名为 Project1 的项目文件 }
uses { 在 program 中需要使用到哪些单元 }
Forms,
Unit1 in 'Unit1.pas' {Form1} ,
Dialogs;
{ 在这里可以定义一些变量和常量 }
var
AppMsg: String ;
{$R *.res} {$R 是一个编译指令 , 此处表示要编译资源文件 Project1.res}
{ 这个部分可以实现一个函数和过程 }
procedure AppStart(AppMsg: String );
begin
ShowMessage(AppMsg);
end ;
{begin…end 部分是 program 的主体 , 这里面的代码是可以运行的 }
begin
{ 应用程序初始化 }
Application.Initialize;
Application.Title := 'lxpbuaa';
{ 创建主窗体 }
Application.CreateForm(TForm1, Form1);
AppMsg := ' 应用程序马上开始运行。 ';
AppStart(AppMsg);
{ 应用程序开始运行 }
Application.Run;
end .
Unit 的组织结构
一个单元文件被关键字 unit 标识,因此,在这里我将单元文件的源代码称作 unit 。以下是一个完整的 unit :
unit Unit1; { 文件定义 : 一个名为 Unit1 的单元文件 }
interface { 在这个部分声明可供其他单元使用的变量、常量、类型、函数和过程 }
uses {interface 部分的 uses 内容对整个单元都有效 }
Windows, Messages, Classes, Controls, Forms, Dialogs, Contnrs;
type { 声明类型 }
TForm1 = class (TForm)
private
procedure ShowInfo(Info: String );
public
{ Public declarations }
end ;
{ 声明函数和过程 }
procedure ShowInfo(Info: String );
var { 声明变量 }
Form1: TForm1;
implementation { 在这个部分完成单元的私有声明 , 并实现 interface 声明的类、函数和
过程 }
uses {implementation 部分的 uses 内容只对 implementation 有效。
interface 不需要而 implementation 需要的单元应该在这里引用 }
SysUtils, Variants;
var {implementation 部分可以和 interface 部分一样进行声明 }
ObjList: TObjectList;
{$R *.dfm} { 编译对应的 dfm 文件 }
{ 实现 interface 部分声明的函数和过程 }
procedure ShowInfo(Info: String );
begin
ShowMessage(Info);
end ;
{ 实现 interface 部分声明的类 }
{ TForm1 }
procedure TForm1.ShowInfo(Info: String );
begin
ShowMessage(Info);
end ;
{ 单元初始化部分 }
initialization
ObjList := TObjectList.Create;
{ 单元终止部分 }
finalization
FreeAndNil(ObjList);
end .
从上面的 Unit1 单元可以看到,一个 unit 可以包含五个部分:
( 1 ) unit 关键字部分,指定单元的名字。
( 2 ) interface 部分。从关键字 interface 到 implementation 为止的内容,都是属于这个部分。该部分可以声明变量、常量、类型、函数和过程,而且它们对于其他单元都是可见的。
( 3 ) implementation 部分。在这个部分也可以完成 interface 具有的声明功能,但是它们对于其他单元是不可见的,属本单元私有;同时完成类、函数和过程的实现。
以上三个部分是一个 unit 必须的。接下来的两个部分是可选的。
( 4 ) initialization 。在这个部分可以完成单元的初始化工作。如果将一个单元比作一个类,我们知道类的初始化是在构造函数 Create 中完成的,所以 initialization 部分就相当于单元的构造函数。
( 5 ) finalization 。在这个部分可以完成单元的终止,完成类似于类的析构函数 Destroy 的功能。
需要注意的是:如果几个单元都有 initialization/finalization 部分,则它们的执行顺序与这些单元在 program 的 uses 字句中的出现顺序一致。所以应该避免 initialization/finalization 部分的代码执行时依赖于它们的执行顺序。
单元循环引用
单元不能被循环引用 ( Circular unit reference ) 的。循环引用的意思是 : A 引用了 B , 而 B 又引用 A , 且都是在 interface 部分进行引用。如下面的单元通不过编译:
unit Unit1;
interface
uses
Unit2;
……
unit Unit2;
interface
uses
Unit1;
……
但是如果引用不全发生在 interface 部分,即至少有一个在 implementation 部分,则是允许的。因此,当你需要两个单元相互引用时,应该将其中的一个引用放置在 implementation 部分,否则不能通过编译。
为了避免将来可能的循环引用,对于只在实现部分使用的单元,通常我们都将它写在 implementation 而不是 interface 部分。例如我们编写一个取得一个整型动态数组中最大的元素,需要用到 Math 单元的 Max 函数,此时代码书写应该按照如下所示:
unit Unit2;
interface
{ 因为在声明部分只有一个函数 GetMax , 只须引用 System 单元 , 而该单元是自动引用的 , 所以
在源代码中 , 接口部分没有任何引用单元的代码行 }
function GetMax(IntDynArray: Array of Integer): Integer;
implementation
uses Math; { 在这里而不是接口部分引用单元 Math}
function GetMax(IntDynArray: Array of Integer): Integer;
var
L,I: Integer;
begin
Result := 0;
L := Length(IntDynArray);
if L = 0 then Exit
else
begin
Result := IntDynArray[Low(IntDynArray)];
for I := Low(IntDynArray)+1 to High(IntDynArray) do
Result := Max(Result, IntDynArray[I]);
end ;
end ;
end .