l Delphi 中最常见的源文件的结构及相关的注意事项。
l 本书会用到的两种程序类型 可能看起来这些东西都非常浅显,但对于初学者而言,缺乏这些知识可能连在 Delphi
中运行本书上的示例都做不到。但是,若读者觉得自己这方面还可以的话,那就完全没有 必要把时间花在第一章。根据我本人的经验,在阅读任何一本书时,第一章是最费时间的 部分,若能用这种方法跳过第一章岂不是很好?
工程文件结构与语法
一个完整的可执行的 Delphi 程序由多个 unit 模块组成,这些模块被一个单一的源文 件——工程文件所联系。不同于传统的 Pascal 程序,Delphi 中程序的主模块的源文件格 式为.dpr,其它模块的格式为.pas。一个完整的程序 = 一个.dpr 文件 + 0 或多个.pas 文件。
一个.dpr 文件的组成部分为:程序头、[uses 从句]、主程序块。(另外,在 IDE 中, 一个工程中往往需要用到命名空间的限定语句。关于此我们后述。)一个典型的工程文件 如下:
program Project2;
{$APPTYPE CONSOLE}
uses
SysUtils; begin
end. 下面我们详细介绍这三个部分:
程序头语句指示所编写的程序的名称。形式为:project 程序名; 在 RAD 中,此名称在 任何时候均与.dpr 文件名相同。良好的程序中,.dpr 中的代码应当尽量少,尽量将代码分 于.pas 中然后在.dpr 中调用。
在程序主文件中使用到的所有.pas 文件的名称必需列在 uses 从句中。其形式为: uses name1, name2, „ ;
- 7 -
Delphi 中用 begin 与 end 所包围起的一段代码称为一个代码块。工程文件含有一个主 程序块用于存放主程序的代码。工程文件中的全局标识符必须声明于 begin 之前。
单元文件结构
程序的每个子模块称之为一个单元。单元中可包含任何元素包括类型、变量、常量、 例程等。每个单元均存储于一个.pas 文件中。一个文件只能对应一个单元(就像一件衣服 只能同时供一个人穿)。
单元文件的后缀名为.pas。每个.pas 文件包依次含以下部分:单元名称、Interface 关键字、implementation、 [initialization]、 [finalization]、关键字 end。一个完
整的.pas 文件的结构如下: unit Unit1; //单元的名称
interface //Interface 部分起始处 uses „
„„
implementation //Implementation 部分起始处 uses „
„„
initialization //initialization 部分起始处
„„
finalization //finalization 起始处
„„
end. //单元文件结尾(end 后必须以句号结尾)
形式如 unit 名称;此处的名称与.pas 文件的名称也一致。注意一个.dpr 中不能引用两 个名称相同的.pas。
关键字 Interface 至关键字 implementation 之间的部分称为 Interface 部分。 Interface 部分可定义变量、常量、例程等。单元中并非仅有此处才可定义标识符,但只 有此部分中定义的标识符才可被其它文件访问。值得注意的是此部分定义的例程仅仅只需 留下一个原型即可,具体的实现部分则放于接下来的 implementation 部分。此部分可以书 写 uses 从句,其作用与.dpr 中相同。注意 uses 从句必须紧接于关键字 Interface 后,二 者之间不允许有任何代码。
关键字 implementation 至关键字 initialization 之间的部分。此部分可书写例程的 上体代码,但顺序上不必完全与声明时一致。此处实现时可以省略参数,但若未省略则必
- 8 -
须与声明时完全一致。与 interface 部分类似,此部分可以定义常量、变量、例程等,但 只能在本单元内使用,本单元的 inteface 部分或其它单元无法使用此处定义的标识符。当
然也可以合用从句,但只能紧跟关键字 implementation 之后。
关键字 initialization 至关键字 finalization 之间的部分。此部分可选。 此部分中 含有的代码在程序启动时运行,若多个单元中均含有,则按照工程文件 uses 从句中引用的 顺 序 来运 行 。此 关键 字也 可 直接 用 begin 代 替, 不 过如 此 一为 ,将 无法 指 定单 元 的 finalization,在此种情形下,可以用 EXITPROC 变量来模拟(将要执行的代码封装为一个 过程然后将此过程赋予指针 ExitProc)。
finalization 至.pas 文件的末尾。此部分可有可无,仅当 initialization 部分存在 时才可使用此部分。程序退出时运行此部分的代码,除非程序是由 Halt 例程强行退出。当 多个单元均存在此部分时,执行顺序与 initialization 相反。
单元语法与 uses 从句
uses 从句中列出了所有使用到的单元的名称(不是源文件名称)。uses 从句可以出现
(并非一定要出现)于 3 种场合,分别是:
l .dpr 文件中工程名称的后面
- .pas 文件 Interface 部分,必须紧跟于关键字 Interface
- .pas 文件 implementation 关键字之后
System 与 SysInit 单元被默认引用,故无需手动引用。在 IDE 中新建一个工程时,IDE 会 自动引用一些常见单元以减轻代码编写量。
uses 从句中的单元寻找时按照一定的规则。
uses 后接单元名称即可,单元名称间以逗号隔开,最后以分号结尾。如 uses unti1, unit2; 在.dpr 文件中,uses 后引用的单元名称后可加上源文件的名称如: unit1 in '„
\unit1.pas',in 用于指定单元所在的源文件。IDE 默认源文件为当前目录下的同名.pas 文件或是搜索路径中的同名.pas 文件,但有时并非如此,此时就需要 in 来限定。如下情 形:
l 源文件不在当前目录及搜索目录中
l 不同的目录具有相同名称的源文件
在.dpr 中以 in 限定的源文件会被认为是工程的一部分。这不影响编译,但影响 IDE 对工 程的管理。
在.pas 不能使用 in,这意味着此类型文件中引用的所有单元的源文件均在当前目录或搜索 路径之中。
- 9 -
单元在 uses 从句出现的顺序影响单元中 intialization 部分语句的执行顺序及编译器 对标识符的定位。若多个单元中定义了相同的标识符,编译器取最后出现的单元,对于其 它单元中的标识符,必须添加限定。
前面说过,uses 从句中列举出所有直接用到的单元名称。请注意这句话:直接用到。 也就是说,若 unitA 用到了 unitB 中的标识符,则 unitB 必须出现于 unitA 的 uses 从句 中,但若 unitB 使用了 unitC 中的标识符而 unitA 未用到 unitC 的标识符,此时 unitC 不 必出现于 unitA 的 uses 从句中。
当单元的接口部分发生变体时所有依赖此单元单元的文件均需被重新编译,但若变体 只是在实现部分,则不需要此过程。
当一个单元中引用了另一个单元时,称这两个单元为相互引用。Delphi 不允许循环引 用。所谓循环引用,是指一个单元直接或间接地引用了自己,如:单元 A 引用了单元 B, 单元 B 引用单元 C,而单元 C 又引用了单元 A。关于循环引用,最常见的错误是两个单元相 互引用对方:A 引用 B,而 B 又引用 A。当然,无论是间接引用还是循环引用均是指 Interface 部分,对于 implementation 不存在这种说法。为避免循环引用,建议手动引用 某个单元时,在 implementation 部分的 uses 从句中列出。
常见程序类型
启动 Delphi2010,依次点击:File—>New—>Other,此时弹出"New Item"对话框",双 击其中的"Console Application",IDE 会自动新建一个包含一个.dpr 源文件的命令行程 序,其代码为:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils; begin
try
{ TODO -oUser -cConsole Main : Insert code here } except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message); end;
end. 为了方便,我们将上述代码中粗体部分全部删除,将代码简化为如下形式: program Project1;
- 10 -
{$APPTYPE CONSOLE}
uses
SysUtils; begin
end.
至此,我们得到了一个简单的程序。读者可按下键盘上的 F9 功能键或点击 Run 菜单中的 Run 命令来运行此程序。为了节省篇幅,在接下来的示例中,我们将省略程序名称及 uses 从句。也就是说,对于如下代码:
var
x,y:integer; begin
x := 9;
y := 7;
writeln(x+y); readln;
end. 读者应当清楚,这段代码的完全形式为: program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
x,y:integer; begin
x := 9;
y := 7;
writeln(x+y); readln;
end. 说明:
l 在命令行程序中我们使用传统的标准例程 writeln 在屏幕上显示内容。如上例中的 writeln(x+y)将在屏幕上显示 x+y 的值。同样,writeln('delphi')可在屏幕上显示 delphi。
l 标准例程 readln 用于使屏幕暂停,按回车键可结束暂停。读者可将此句代码去掉后运 行程序以感受这句代码的作用。少数情形下 readln 可能无法暂停屏幕,此时可使用
read(V)代替。其中的 V 是在 var 中声明的变量。如上例中的 readln 也可替换成 read(y);不过在这种情形下,按下回车键并不能结束暂停,最保险的方法是用鼠标手 动关闭命令行屏幕。
在 NewItem 对话框中双击"VCL Forms Application",IDE 自动新建一个窗体程序。如
- 11 -
下图所示:
注意到新建的窗体周围有 6 个方形块,拖动右下角的空心方形可调节窗体大小。
自 IDE 右上方的 Project Manager 工具窗口可看到此工程包含一个 Unit1.pas 源文 件。IDE 容器的底样有三个标签,分别是 Code/Design/History,单击 Code 标签可查看此 窗体所对应的单元文件即 Unit1.pas 的代码:
unit Unit1; interface uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs;
type
TForm1 = class(TForm) private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm} end.
- 12 -
若要查看此工程的.dpr 文件可用鼠标指向此窗口中的 Project1.exe,选择 View Source 命 令即可。其源代码为:
program Project1; uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res} begin
Application.Initialize; Application.MainFormOnTaskbar := True; Application.CreateForm(TForm1, Form1); Application.Run;
end.
读者可将此两份代码与前面所介绍的.dpr 文件与.pas 文件的基本结构对照看一下是否 符合。现阶段我们只使用工程的.pas 文件而不需要使用.dpr 文件。
同命令行程序一样,可以按下 F9 或选择 Run 菜单下的 Run 命令来运行这个窗体程序, 得到的结果是一个空白的窗体如下图:
当工程处于 Design 状态时可在 IDE 右下方的 Tool Palette 中选择控件,然后在空白窗体 中按下鼠标并拖动,如在 Tool Palette 的 Standard 类别中选择 TButton 控件然后拖动鼠 标,可以在窗体上添加一个按钮,结果如图:
- 13 -
按钮周围的 8 个蓝色小圆珠有两个作用:一是表示当前焦点在此按钮上,二是通过拖动这
些小圆珠可调节按钮大小。这与窗体周围的 6 个小方块的作用相同。 当焦点停留于窗体或窗体内的控件上时,IDE 左下方的 Object Inspector 工具窗口中
会自动显示当前控件或窗体的属性,如标题、名称、大小等,可以手动设置这些值。
注意 Object Inspector 工具窗口亦有两个标签分别是 Properties 与 Events,默认情 形为 Properties,表示此工具窗口中显示的是控件的属性;点击 Events,工具窗口中将显 示控件的事件,其中有 OnClick、OnEnter 等,双击 OnClick 右侧的下拉列表,可在窗体对
应的.pas 文件中产生此事件的框架:
procedure TForm1.FormClick(Sender: TObject); begin
end; 当运行时点击按钮即可触发此事件。例如在其中加入显示对话框的代码: procedure TForm1.FormClick(Sender: TObject);
begin
showmessage('my first delphi form'); end;
按下 F9 运行并点击按钮,会弹出如下对话框: