源代码书写规范

Delphi代码标准文档
Revision 1.0.0.5
编者:Riceball(riceball@cq118.com)

  本文主要基于“Delphi Development Guide”一书的代码标准部分而来,编者参考其它的Delphi代码标准规范,以及结合现目前Delphi社区中流行的程序书写惯例,作了一些修改和裁剪,你也可以根据自己的需要进行裁剪和修改。如果你有什么建议,或是希望编者加上你认为遗漏的东西,欢迎Email至:riceball@cq118.com

目录:

  1. 前言
  2. 源程序书写规范
    1. 通用源代码书写规范
    2. Object Pascal语句书写规范与用法
  3. 命名规范
    1. 过程(Procedure)与函数(Function)
    2. 变量(Variable)
    3. 类型(Type)
      1. 一般类型
      2. 构造类型
      3. 类类型(Class)
        1. 字段
        2. 方法
        3. 属性
      4. 元件类型
      5. 窗体与对话框类型
      6. 数据模块类型
    4. 文件
      1. 项目文件(.dpr)
      2. 窗体文件(.dfm)
      3. 数据模块文件
      4. 远程数据模块文件
      5. 单元文件(.pas)
        1. 普通单元
        2. 窗体单元
        3. 通用单元
        4. 元件单元
      6. 包文件(.dpk)
  4. 源程序文档注释规范
    1. 注释文档的一般规范
    2. 单元文件注释文档格式
    3. 函数(属性)的注释文档格式
  5. Delphi代码自动格式化工具
  6. 代码标准文档更新
  7. 附录:参考文献

 


1.前言

  本文档主要是为Delphi开发人员提供一个源代码书写标准,以及程序和文件的命名标准,使他们在编程时有一致格式可遵循。这样,每个编程人员编写的代码能够被其他人理解。

本文档还没能包括代码标准的每个细节,因此你可以在下面网址留意本文档的最新版本:
http://dev.cq118.com/web/ 

  本文档不包含用户界面标准。用户界面标准是独立于其他标准的,并且同样是重要的。

  如果你有什么建议,或是希望编者加上你认为遗漏的东西,欢迎Email至:riceball@cq118.com


2.源程序书写规范

2.1.通用源代码格式规则

1). 缩进

  缩进就是每级间有两个空格。不要在源代码中放置制表符。这是因为,制表符的宽度随着不同的设置和代码管理实用程序(打印、文档及版本控制等)而不同。

  通过使用Tools|Environment 菜单,在Environment Options 对话框的General页上,不要选中Use Tab Character 和Optional Fill 复选框,这样,制表符就不会被保存。

2). 边距

  边距设置为80个字符。源代码一般不会因写一个单词而超过边距,但本规则比较灵活。只要可能,长度超过一行的语句应当用逗号或运算符换行。换行后,应缩进两个字符。

3). begin...end 语句

begin 语句必须单独占一行。例如,下面第一行是错误的,而第二行正确:

for i:=0 to 10 do begin // 错, begin 与f o r 在同一行

for i:=0 to 10 do // 对, begin 在另外一行中
begin

本规则的一个特殊情况是,当beginelse 语句的一部分时,例如:

if some statement = then
begin

  . . .
end
else begin

  Some Other Statement;
end;

注意:end 语句总单独一行。当begin 不为else 语句的一部分时,相应的end 语句与begin 语句的缩进量相同。

4).注释

  我们通常使用“{...}”类型的块注释,以前的“(*...*)”类型的块注释用于临时注释掉暂不使用的代码,从Delphi 2开始支持“//”行注释,如果决定不在支持Delphi 2.0以下的版本,可以使用“//”注释。

2.2.Object Pascal语句格式语句书写规范与用法

1). 括号

  在左括号与下一字符之间没有空格。同样,右括号与前一字符也没有空格。下面的例子演示了正确与不正确的空格。

CallProc( Aparameter ); // 错!
CallProc(Aparameter); // 正确!

  不要在语句中包含多余的括号。在源代码中,括号只有在确实需要时才使用。下面的例子演示了正确与不正确用法:

if (I=42) then // 错,括号是多余的
if (I=42) or (J=42) then // 正确,必须使用括号

2). 保留字和关键字

  Object Pascal 语言的保留字和关键字总是完全的小写。下面是Delphi 5保留字列表:

and

array

as

asm

begin

case

class

const

constructor

destructor

dispinterface

div

do

downto

else

end

except

exports

file

finalization

finally

for

function

goto

if

implementation

in

inherited

initialization

inline

interface

is

label

library

mod

nil

not

object

of

or

out

packed

procedure

program

property

raise

record

repeat

resourcestring

set

shl

shr

string

then

threadvar

to

try

type

unit

until

uses

var

while

with

xor

private

protected

public

publishedautomated  

 

3). 过程和函数

(1). 格式

  过程名应当以大写字母开始,且大小写交错以增加可读性。下面是一个不正确的写法:

procedure thisisapoorlyformattedroutinename;

改成这样写就对了:

procedure ThisIsMuchMoreReadableRoutineName;

(2). 形参

(1) 格式

  只要可能,同一类型的形参应当归并在一起:

procedure Foo(Param1,Param2,Param3:Imteger;Param4:string);

(2) 参数顺序

  形参的顺序主要要考虑寄存器调用规则。最常用的参数应当作为第一个参数,按使用频率依次从左到右排。输入参数位于输出参数之前。范围大的参数应当放在范围小的参数之前。例如:

SomeProc(aPlanet, aContinent, aCountry, aState, aCity).

  有些则例外。例如,在事件处理过程中,TObject 类型的Sender 参数往往是第一个要传递的参数。

(3) 常量参数

  要使记录、数组、短字符串或接口类型的参数不能被过程修改,就应当把形参标以Const 。这样,编译器将以最有效的方式生成代码,保证传递的参数不可变。

  如果其他类型的参数希望不被过程所修改,也可以标上Const 。尽管这对效率没有影响,但这给过程的调用者带来了更多的信息。

4). 变量

(1). 局部变量

  局部变量用于过程内部,果需要的话,应当在过程的入口处立即初始化变量。局部的AnsiString 类型的变量自动被初始化为空字符串,局部的接口和dispinterface类型的变量自动被初始化为nil,局部的Variant和OleVariant类型的变量自动被初始化为Unassigned。

(2). 全局变量

  一般不鼓励使用全局变量。不过,有时候需要用到。即使如此,也应当把全局变量限制在需要的环境中。例如,一个全局变量可能只在单元的实现部分是全局的。

  全局数据如果将由许多单元使用,就应移动到一个公用单元里被所有对象使用。全局数据可在声明时直接初始化为一个值。注意,所有全局变量自动进行零初始化,因此,不要将全局变量初始化为诸如0 、nil、或Unassigned等空值。零初始化的全局变量在.EXE文件中不占空间。零初始化的数据保存在虚拟的数据段中,而虚拟数据段只在应用程序启动时才分配内存。非零初始化的全局数据则在.EXE文件中占空间。

5). 类型

(1). 大小写规则

  类型标识符是保留字,应当全部小写。Win32 API 类型常常全部大写,并且遵循诸如Windows.pas或其他API单元中关于特定类型名的规则。对于其他变量名,第一个字母应大写,其他字母则大小写交错。下面是一些例子:

var
  MyString: string; // 保留字
  WindowsHandle: HWND; // Win32 API 类型
  I: Integer; //在System单元中引入的类型标识

(2). 浮点型

  不鼓励使用Real类型,因为它只是为了与老的Pascal代码兼容而保留的。通常情况下,对于浮点数应当使用Double。Double可被处理器优化,是IEEE定义的标准的数据格式。当需要比Double提供的范围更大时,可以使用Extend。Extend是intel专用的类型,Java不支持。当浮点变量的物理字节数很重要时(可能使用其他语言编写DLL),则应当使用Single。

(3).Variant和OleVariant

  一般不建议使用Variant和OleVariant。但是,当数据类型只有在运行期才知道时(常常是在COM和数据库应用的程序中),这两个类型对编程就有必要。当进行诸如自动化ActiveX控件的COM编程时,应当使用OleVariant;而对于非COM编程,则应当使用Variant。这是因为,Variant能够有效地保存Delphi的原生字符串,而OleVariant则将所有字符串转换为OLE字符串(即WideChar字符串),且没有引用计数功能。

6). 语句

(1). If 语句

  在if/then/else语句中,最有可能执行的情况应放在then子句中,不太可能的情况放在else子句中。为了避免出现许多if语句,可以使用case语句代替。如果多于5级,不要使用if语句。请改用更清楚的方法。不要在if语句中使用多余的括号。

  如果在if语句中有多个条件要测试,应按照计算的复杂程度从右向左排。这样,可以使代码充分利用编译器的短路估算逻辑。例如,如果Condition1比Condition2快,Condition2比Condition3快,则if语句一般应这样构造:

if Condition1 and Condition2 and Condition3 then

  如果Condition3为False的机会很大,利用短路估算逻辑,我们也可以将Condition3放在最前面:

if Condition3 and Condition1 and Condition2 then

(2). case 语句

(1) 概述

  case语句中每种情况的常量应当按数字或字母的顺序排列。每种情况的动作语句应当简短且通常不超过4 - 5 行代码。如果动作太复杂,应将代码单独放在一个过程或函数中。Case语句的else子句只用于默认情况或错误检测。

(2) 格式

  case语句遵循一般的缩进和命名规则。

(3). while 语句

  建议不要使用Exit过程来退出while循环。如果需要的话,应当使用循环条件退出循环。所有对while循环进行初始化的代码应当位于while入口前,且不要被无关的语句隔开。任何业务的辅助工作都应在循环后立即进行。

(4). for 语句

  如果循环次数是确定的,应当用for语句代替while语句。

(5). repeat 语句

  repeat语句类似于while循环,且遵循同样的规则。

(6). with 语句

(1) 概述

  with语句应小心使用。要避免过度使用with语句,尤其是在with语句中使用多个对象或记录。例如:

with Record1,Record2 do

  这些情况很容易迷惑编程人员,且导致调试困难。

(2) 格式

  with语句也遵循本章关于命名和缩进的规则。

7). 结构化异常处理

(1). 概述

  异常处理主要用于纠正错误和保护资源。这意味着,凡是分配资源的地方,都必须使用try...finally来保证资源得到释放。不过,如果是在单元的初始/结束部分或者对象的构造器/析构器中来分配/释放资源则例外。

(2). try...finally的用法

  在可能的情况下,每个资源分配应当与try...finally结构匹配,例如,下面代码可能导致错误:

SomeClass1 := TSomeClass.Create;
SomeClass2 := TSomeClass.Create;
try
  { do some code }
finally
  SomeClass1.Free;
  SomeClass2.Free;
end;

  上述资源分配的一个安全方案是:

SomeClass1 := TSomeClass.Create;
try
  SomeClass2 := TSomeClass.Create;
  try
    { do some code }
  finally
    SomeClass2.Free;
  end;
finally

  SomeClass1.Free;
end;

  但有时如果有许多类都需要同时创建,上述方案就显得非常累赘。这时候,我建议使用下述的一个安全方案:

SomeClass1 := nil;
SomeClass2 := nil;
SomeClass1 := TSomeClass.Create;
SomeClass2 := TSomeClass.Create;
try
  { do some code }
finally
  FreeAndNil(SomeClass1);
  FreeAndNil(SomeClass2);
end;

(3). try...except的用法

  如果你希望在发生异常时执行一些任务,可以使用try...except。通常,没有必要为了简单地显示一个错误信息而使用try...except,因为Application对象能够自动根据上下文做到这一点。如果要在子句中激活默认的异常处理,可以再次触发异常。

(4). try...except...else的用法

  不鼓励使用带else子句的try...except,因为这将阻塞所有的异常,包括你没有准备处理的异常。

3.命名规范

3.1.过程(Procedure)与函数(Function)

1).命名

  过程与函数名应当有意义。进行一个动作的过程最好在名称前加上表示动作的动词为前缀。例如:

procedure FormatHardDrive;

  设置输入参数值的过程名应当以Set 为其前缀,例如:

procedure SetUserName;

  获取数值的过程名应当以Get 为其前缀,例如:

function GetUserName:string;

2).形参

  所有形参的名称都应当表达出它的用途。如果合适的话,形参的名称最好以字母a 为前缀,例如:

procedure SomeProc(aUserName:string; aUserAge:integer);

  当参数名与类的特性或字段同名时,前缀a 就有必要了。

3).命名冲突

  当两个单元中含有相同名称的过程时,如果调用该过程,实际被调用的是Uses 子句中较后出现的那个单元中的过程。为避免这种情况,可在方法名前加想要的单元名,例如:

SysUtils.FindClose(SR);

或Windows.FindClose(Handle);

3.2.变量(Variable)

  变量的名称应当能够表达出它的用途。循环控制变量常常为单个字母,诸如I 、J 或K 。也可以使用更有意义的名称,例如UserIndex。布尔变量名必须能清楚表示出True 和False 值的意义。

1). 局部变量

  局部变量遵循其他变量的命名规则。

2). 全局变量

  全局变量以大写字母“G”打头,并遵循其他变量的命名规则。

3.3.类型(Type)

3.3.1.一般类型

1). 枚举型

  枚举类型名必须代表枚举的用途。名称前要加T字符作为前缀,表示这是个数据类型。枚举类型的标识符列表的前缀应包含2 - 3 个小写字符,来彼此关联。例如:

TSongType=(stRock, stClassical, stCountry, stAlternative, stHeavyMetal, stRB);

  枚举类型的变量实例的名称与类型相同,但没有前缀T ,也可以给变量一个更加特殊名称,诸如:FavoriteSongTypel、FavoriteSongType2等等。

3.3.2.构造类型

1). 数组类型

  数组类型名应表达出该数组的用途。类型名必须加字母“T”为前缀。如果要声明一个指向数组类型的指针,则必须加字母P 为前缀,且声明在类型声明之前。例如:

type
  PCycleArray = ^TCycleArray;
  TCycleArray=array[1..100] of integer;

实际上,数组类型的变量实例与类型名称相同,但没有“T”前缀。

2). 记录类型

  记录类型名应表达出记录的用途。类型名必须加字母T为前缀。如果要声明一个指向记录类型的指计,则必须加字母P为前缀,且其声明在类型声明之前。例如:

type
  PEmployee = ^TEmployee;
  TEmployee = record
    EmployeeName: string;
    EmployeeRate: Double;
  end;

3.3.3.类类型(Class)

1). 命名与格式

  类的名称应当表达出类的用途。一般的类名前要加字母“T”,如果是接口类那么类名前要加“I”,错误异常类的类名前要加“E”,而类引用类型(Class-reference type)则要在类名后加“Class”,抽象类一般是在类名前还要加“Custom”。例如:

type
  TCustomCipher = class(TObject);
  TCipher = class(TCustomCipher);
  ICipher = interface;
  TCipherClass = class of TCustomer
  ECipherException = class(Exception);

  类的实例名称通常与类名相同,只不过没有前缀“T”。

var
  Customer: TCustomer;

注意:关于元件的命名,请参阅“元件类型”。

2).字段

(1) 命名与格式

  字段的命名遵循与变量相同的规则,只不过要加前缀F ,表示这是字段。

(2) 可见性

  所有字段必须为私有。如果要在类的作用域之外访问字段,可借助于类的属性来实现。

3).方法

(1) 命名与格式

  方法的命名遵循与过程和函数相同的规则。

(2) 静态方法

  当你不希望一个方法被派生类覆盖时,应当使用静态方法。

(3) 虚拟方法(virtual)与动态方法(dynamic)

  当你希望一个方法能被派生类覆盖,应当使用虚拟方法(virtual)。如果类的方法要被多个派生类直接或间接地使用,则应当用动态方法(dynamic)。例如,某一个类含有一个被频繁覆盖的方法,并有100个派生类,则应将方法定义为动态的,这样可以减少内存的开销。

(4) 抽象方法(abstract)

  如果一个类要创建实例,则不要使用抽象方法。抽象方法只能在那些从不创建实例的基类中使用。

(5) 属性访问方法

  所有属性访问方法应当定义在类的私有或保护部分。属性访问方法遵循与过程和函数相同的规则。用于读的方法应当加“Get”前缀,用于写的方法应当加“Set”前缀,并且有一个叫Value的参数,其类型与属性的类型相同。例如:

TSomeClass = class(TObject)
private
FSomeField: Integer;
protected
function GetSomeField: Integer;
procedure SetSomeField(Value: Integer);
public
property
SomeField: Integer read GetSomeField write SetSomeField;
end;

  尽管不是必须,但还是建议你使用写访问方法来访问代表私有字段属性。

4).属性

  属性作为私有字段的访问器,遵循与字段相同的命名规则,只不过没有F前缀。属性名应为名词,而不是动词。属性是数据,而方法是动作。数组属性名应当是复数,而一般的属性应当是单数。

3.3.4.元件类型

1). 元件类型的命名标准

  元件的命名与类的命名类似,只不过当它与其它元件名称冲突时,你可以加上3个字符的前缀,用以标识公司、个人或其他实体。例如,一个时钟元件可以这样声明:

TddgClock = class(TComponent)

注意,作为前缀的3 个字符要小写。

2). 元件实例的命名规则

  元件实例的名称应当能够描述其实际意义,这里命名规则使用了一个变更的匈牙利前缀命名规范。使用前缀而不使用后缀的原因是在搜寻时,在对象检查器和代码探索器中搜寻构件的名字比搜寻构件的类型更容易。在这个标准中,元件实例名包括两个部分:前缀和性质标识名。

(1). 元件的前缀

  元件的前缀多是表现元件类型的字母缩写。参见下面表中的元件前缀:

元件类名元件前缀
TActionList, TAction表示动作的列表项act
TButton, TSpeedButton, TBitBtn等所有的按钮类btn
TCheckBox, TDBCheckBox等所有的检查框chk
TRadioButton单选按钮类rdo
TToolBar工具条tb
TMainMenu所有的主菜单类mm
TMainMenuItem所有的菜单项类mi
TPopupMenu所有的弹出式菜单类pm
TPopupMenuItem所有的弹出式菜单项类pmi
TLabel, TStaticText等所有用来显示的标签类lbl
TPanel等所有的面板类pnl
TPageControl等所有的页式控件类pgc
TEdit, TMaskEdit等所有的单行编辑框类edt
TMemo, TRichEdit等所有的多行编辑框类mmo
TDrawGrid, TStringGrid等所有的网格类grd
TAnimate等所有的动画类ani
TImageList等所有的图片列表类il
TImage等图片类img
TChart图表类cht
TComboBox, TDBComboBox等所有的下拉式列表框类cbo
TListBox, TDBList等所有的列表框类lst
TTreeViewtv
TListViewlv
THotKeyhk
TSplitter等所有的分隔符类spt
TOpenDialog等所有的对话框元件类dlg
TTable等所有的数据表类tbl
TQuery等所有的SQL查询类元件qry
TClientDataSet所有的客户数据集元件cds
TDataSourceds
TDatabasedb
TSockConnection,TDCOMConnection等连接元件类con
TQuickRep, TFastReport等所有的报表元件类rpt
TDDEClientConv,TDDEClientItem等所有的DDE元件类dde
TMonthCalendar等所有的日历类cal
TGroupBox等控件类grp

  如上所示,元件类型前缀是从分析描述元件的类型性质而来的。通常情况下,下面的规则描述如何定义一个元件类型前缀:

注意:元件的前缀是为了表示出元件的类型,是按钮,还是标签等等,因此没有必要为每一个特别元件类建立一个元件前缀,如: TMyButton的元件前缀仍为btn。

(2). 元件性质标识名

  元件性质标识名是元件意图的描述。例如,一个用于关闭窗体的TButton元件实例可命名为btnClose。一个编辑姓名的元件实例可命名为edtName。

3.3.5.窗体与对话框类型

1). 窗体类型的命名标准

  窗体或对话框类型的名称应当表达出窗体的用途,如果是窗体要加“Tfrm”前缀,如果是对话框要加“Tdlg”,后跟描述性名。例如,About窗体类型名称为:

TfrmAbout = class(TForm)

主窗体的类型名称为:

TfrmMain = class(TForm)

客户登录窗体的类型名称为:

TfrmCustomerEntry = class(TForm)

登陆对话框的类型名称为:

TdlgLogin = class(TForm)

2). 窗体实例的命名标准

  窗体实例的名称与相应的类型名称相同,但没有前缀T 。例如,前面提到的窗体类型与实例的名称为:

类型名实例名
TfrmAboutfrmAbout
TfrmMainfrmMain
TfrmCustomerEntryfrmCustomerEntry
TdlgLogindlgLogin

3). 自动创建的窗体

  除非特别原因,只有主窗体才自动生成。其他所有窗体必须从Project Options对话框的自动生成列表中删除。更进一步信息,请参阅后面几节。

4). 模式窗体实例化函数

  所有窗体单元都应当含有实例化函数,用于创建、设置、模式显示和释放窗体。这个函数将返回由窗体返回的模式结果。传递给这个函数的参数遵循参数传递的规则。之所以要这样封装,是为了便于代码的重用和维护。

  窗体的变量应当从单元中移走,改在窗体实例化函数中作为局部变量定义(注意,要求从Project Options对话框的自动生成列表中移走该窗体。请看前面的内容。

例如,下面的单元文件演示了GetUserData的实例化函数。

Unit UserDataFrm;

Interface

Uses
 
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

Type

  TfrmUserData = class(TForm)
    edtUserName: TEdit;
    edtUserID: TEdit;
  private
 
{ Private declarations }
  public
 
{ Public declarations }
  end;

function GetUserData(var aUserName: String;var aUserID: Integer): Word;

implementation

{$R *.DFM}

function GetUserData(var aUserName: String;var aUserID: Integer): Word;
var
 
frmUserData: TfrmUserData;
begin
 
frmUserData := TfrmUserData.Create(Application);
  frmUserData.Caption:='Getting User Data' ;
  Result : = frmUserData.ShowModal;
  if Result=mrOK then
  begin
   
aUserName := frmUserData.edtUserName.Text;
    aUserID := StrToInt(frmUserData.edtUserID.Text);
  end;
  finally
   
frmUserData.Free;
  end;
end;

End.

5).窗体框架与复合窗体

  如果一个窗体结构过于复杂,就必须将其分化成为一个主窗体框架以及嵌入到主窗体框架的若干子窗体框架。如:

TfrmMainFrame: TfrmInfoFrame,TfrmEditorFrame

  使用窗体框架,主要是为了解决界面和代码复用问题,以及提高单元代码的内聚力(划分后,每一个窗体框架为一个独立单元),从而提高软件工程质量。你必须提炼出界面关联代码(可复用的)和应用关联代码(不能复用的)。

3.3.6.数据模块类型

1). 数据模块的命名标准

  数据模块类型名称应表达出它的用途,且要加前缀“Tdm”,后跟描述性名称。例如,Customer数据模块的类型名称为:

TdmCustomer = class(TDataModule)

Orders 数据模块的类型名称为:

TdmOrder = class(TDataModule)

2). 数据模块实例的命名标准

  数据模块实例的名称应当与相应的类型名称相同,但没有前缀T 。例如,前面的数据模块类型、实例名称如下:

类型名称实例名
TdmCustomerdmCustomer
TdmOrderdmOrder

3.4.文件

  建议在所有源文件、项目文件和单元文件使用结构化的文件头信息。一个文件头至少应包含以下信息:

{
Copyright @ Year by Authors
}

  详细说明,请参阅:单元文件注释文档格式

3.4.1.项目文件

  项目文件的名称应当具有描述意义。例如,“The Delphi 5 Developer’s Guide Bug Manager ”的项目名称为DDGBugs.dpr,一个系统信息程序的名称为SysInfo.dpr。

3.4.2.窗体文件

  窗体文件的名称应当表达出窗体的用途,且具有Frm后缀。例如,About窗体的文件名叫AboutFrm.dfm,主窗体的文件名叫MainFrm.dfm。

3.4.3.数据模块文件

  数据模块文件的名称应当表达出数据模块的作用,且具有DM后缀。例如,Customers数据模块的文件名叫CustomersDM.dfm。

3.4.4.远程数据模块文件

  远程数据模块文件的名称应当表达出远程数据模块的用途。名称后要加RDM后缀。例如,Customers远程数据模块的文件叫CustomersRDM.dfm。

3.4.5.单元文件

1). 普通单元

(1) 单元名

  单元的名称应当有描述性。例如,应用程序的主窗体单元叫MaimFrm.pas。

(2) Uses 子句

  Interface部分的Uses子句应当只包含该部分需要的单元。不要包含可能由Delphi自动添加的单元名。Implementation部分的Uses子句应当只包含该部分需要的单元,不要有多余的单元。

(3) Interface 部分

  Interface部分应当只包含需要被外部单元访问的类型、变量、过程与函数的声明。而且,这些声明应当在Implementation部分之前。

(4) Implementation 部分

  Implementation部分包括本单元私有的类型、变量、过程与函数的实现。

(5) Initialization 部分

  不要在Initialization部分放置花费时间很多的代码。否则,将导致应用程序启动时显得很慢。

(6) Finalization 部分

  确保释放所有在Initialization部分中分配的资源。

2).窗体单元

  窗体单元文件的名称与相应的窗体名称相同,只是要将前缀变成后缀。例如,About窗体的单元名称叫AboutFrm.pas。主窗体的单元文件名称叫MainFrm.pas。

3).数据模块单元

  数据模块单元文件的名称与相应的数据模块名称相同。例如,数据模块单元的名称叫CustomersDM.pas。

4).通用单元

  通用单元是用于存放函数库,一般类型,或常量而非元件的单元。通用单元的名称应当表达出它的用途,名称前要加“u”前缀。例如,一个实用调试工具单元的名称叫uDebugUtilities.pas,包含全局变量的单元名称叫uCustomerGlobals.pas。

注意,一个项目中单元名称必须是唯一的。通用单元名不能重名。

5).元件单元

(1). 命名

  元件单元应放在单独的路径中,以表明它们是定义元件的单元。它们一般与项目不放在同一路径下。单元文件名称应表达出其内容。

注意,有关元件命名标准的更多信息,请参阅“元件类型的命名标准”

  元件单元只能含有一个主要元件,这是指出现在元件选项板上的元件。其他辅助性的元件或对象也可以包含在同一单元中。

(2). 注册单元

  元件的注册过程应当从元件单元中移走,放在一个单独的单元中。这个注册单元用于注册所有元件、属性编辑器、元件编辑器、向导等。

  元件注册应当在设计期包中进行。因此,注册单元应当包含在设计期包而不是运行期包中。建议注册单元这样命名:

xxxReg.pas

其中,xxx字符前缀,以标识元件包名称或公司、个人、其他实体。例如,注册单元命名为xxxReg.pas。

6).包文件(.dpk)命名规则

(1). 运行期包与设计期包

  运行期包中应当只包含所需要的单元。那些属性编辑器和元件编辑器的单元应当放在设计期包中。注册单元也应当放在设计期包中。

(2). 文件命名标准

  包的命名遵循下列模式:

dcliiiDescvvCn.pkg —设计期包
iiiDescvvCn.pkg    —运行期包

  其中,iii代表一个2-3字符的前缀,用于标识公司、个人或其他需要标识的事情,也可不要;Desc表示该控件包的简短描述;vv代表包的版本号,你可以根据需要取舍;前缀“dcl”表示设计期包,没有该前缀表示运行期包;字母“Cn”表示编译器类型与编译器版本号,如:Delphi5=D5, Delphi4=D4, CBuilder3=C3...。

注意包名称中的lib或std分别表示这是设计期包还是运行期包。例如:

dclrbStdCompsD5.pkg —Delphi 5的设计期包
rbStdCompsD5.pkg     —Delphi 5的运行期包

4.源程序文档注释规范

我们将通过文档(源程序文件联机注释)定义标准的Delphi 类(函数等)的概要设计规范(official Delphi Class API Specification),以及定义Delphi 类(函数等)的编程指南手册。因此,我们可以以两种方式来写程序文档(注释),一是作为概要设计的规范,二是作为编程手册。两种程序文档既有差别,又有它们共同的地方,因此,它们可以同时写在源程序文档注释中,它们两者的差别在下面有说明。

编写 Delphi 概要设计规范

TODO: <略>

编写编程指南文档

在概要设计规范的基础上,增加了教程,例子程序,等等,并对目录结构进行了重新设计组织和整理。

注释文档的一般规范

注释位置

为源程序的相关元素添加文档注释很简单,你只需要将注释放在元素的声明(Interface)部分(如果是接口函数或类方法)或实现(implementation)部分(如果是私有函数)。例如:

(for Object Pascal)

   { The Sort function sorts the list.}
   function MyList.Sort(Options: Integer): BOOLEAN;
   begin
     [...]
   end;

(for C++)

   // The Sort function sorts the list.
   bool MyList::Sort(int Options)
   {
     [...]
   }

注释块

注释块是由一系列的没有被空行分开的单行注释构成的,下面就是一个注释块:
   // This is the first sentence.
   // This is the second,
   // and this the third

而下面的注释则是两个注释块,因为它们之间有一个空行把它们分开了:

   // This is the first sentence.

   // This is the second,
   // and this the third

许多文档注释分析工具(如:Doc-O-Matic)会将注释块内的所有的句子自动连接在一起,形成一文字段落,如:

   // This is the first sentence.
   // This is the second,
   // and this the third

经过分析工具生成的文档文本为:

   This is the first sentence. This is the second, and this the third.

忽略文档注释

当注释行前用“##”开头时,该行注释将被文档注释分析工具忽略,该行不会生成到文档中。如:

   //## This line will be ignored.

包含有如下标识符的注释块也不会被生成到文档中去:

"Ignore Text", "$Log", "$Filename", "$Revision", "$Date", "$Author", "$History", "$Id"

单元文件注释文档格式

用于描述该单元的用途,作者,功能。放于单元文件的最前面:

{
  ##Unit Name: %UNIT%
  ##Initial Date: %DATE%
  Summary
    该单元的摘要说明。

  Description
    该单元的详细说明。

  See Also
    参阅

  Bugs
    已知问题。

  Internal
    内部开发人员参阅,不会对外。

  TODO
    待作事项。

  Author
    Riceball LEE(riceball@cq118.com)
    Riceball LEE(riceballl@hotmail.com)

  Copyright
    Copyright(C) %YEAR% by Riceball LEE

  Current Version
    当前版本号

  History
    版本历史。
}


unit xxx;

函数(属性)的注释文档格式

{
  ##Procedure: %PROCNAME%
  ##Date:      %DAY%-%MONTHSHORTNAME%-%YEAR%
  Summary
    该函数(属性)的摘要说明。

  Parameters
    %ARGUMENTS%

  Returns
    %RESULT%

  Conditions
    调用该方法(函数)可能需要的条件

  Exceptions
    该方法(函数)有可能触发的异常。

  Description
    该函数(属性)的详细说明。

  See Also
    参阅

  Bugs
    已知问题。

  Internal
    内部开发人员参阅,在正式发布的文档中不会发布。

  TODO
    待作事项。

  Author
    Riceball LEE(riceball@cq118.com)
    Riceball LEE(riceballl@hotmail.com)

  Copyright
    Copyright(C) %YEAR% by Riceball LEE

  Current Version
    当前版本号

  History
    版本历史。
}

function xxx;

5.Delphi代码自动格式化工具

尽管大多数的代码自动格式化工具能够帮你重排源程序格式,以及更新保留字和标示符的大小写,但是这最好在使用版本控制前进行,如果你已经使用了版本控制,建议你不要轻易使用代码自动格式化工具,哪怕多一个空格,版本控制工具也会认为该行已被修改,从而给程序管理带来不变。

6.代码标准文档更新

关于Delphi代码标准的最新文档,你可以在 http://dev.cq118.com/web/ 处获得。

7.参考

Delphi 程序设计编码规范 For 《Delphi Development Guide》(E)

Pascal Standards FAQ (E)

JavaDoc (E)

Doc-O-matic Document (E)

Artemis Alliance Delphi Coding Standards (E)

Borland官方Object Pascal书写风格指南 (E)