(转)Delphi 编程规范
Delphi 编程规范
【排版规范】
----------
Delphi XE 以上的版本中都有官方的代码格式化工具(Edit -> Format Source),快捷键是 Ctrl + D,用这个工具排版以后的格式就是标准格式了。该工具没有相关的设置方法,我们遵照默认的设置来对我们的代码进行排版。这里对代码的排版规范做一下总结。
----------
1、缩进
缩进是指在每一级有两个空格。不要在源代码中保留 Tab 字符,这是因为,制表符的宽度会随着不同的用户设置和代码管理实用程序(打印、文档及版本控制等)而不同。
要禁止保存 Tab 字符,可以通过 Tools -> Editor Options 菜单,在 Editor Properties 对话框的 General 页上,取消 Use tab Character 和 Optimal Fill 复选框即可。
----------
2、页宽
默认页宽为 80 字符宽。超出这个范围的代码应该在“逗号”或“操作符”的地方折换到下一行,并相对第一行缩进 2 个字符。声明函数时,参数的名称和类型要成对出现,不能分行书写。例如:
function CreateWindowEx(dwExStyle: DWORD; lpClassName: PChar;
lpWindowName: PChar; dwStyle: DWORD; X, Y, nWidth, nHeight: Integer;
hWndParent: HWND; hMenu: hMenu; hInstance: HINST; lpParam: Pointer)
: HWND; stdcall;
----------
3、代码间隔
各语句元素之间使用空格隔开,以增强程序的可读性。例如:
A:=1; 应写成 A := 1;
遇到如下情况,需要添加空格:
(1) 逗号(,)的后面
(2) 冒号(:)的后面
(3) 等号(=)的前后
(4) 赋值号(:=)的前后
(5) 运算符(+、-、*、/、and、or 等)的前后
不同逻辑的程序块之间要使用空行分隔,空行起着分隔程序段落的作用,适当的空行可以使程序的布局更加清晰。
----------
4、begin ... end 配对
begin else end 语句应独占一行。相应的 end 语句永远缩进到与 begin 语句相对应的位置。例如:
if 条件 then
begin
...
end
else
begin
...
end;
----------
5、括号
不要在“括号”与“括号内字符”之间留下空格。不要在一个语句中使用不必要的括号。例如:
Random( 100 );
应写成
Random(100);
if (x > 10) then
应写成
if x > 10 then
----------
6、保留字和关键字
如果类型的名称是保留字或关键字,那么它应该全部小写。Win32 API 类型通常全部大写,并且必须遵循在 Windows.pas 或其他 API 单元中的详细类型名称的约定。例如:
Mytring: string; // 保留字
WindowsHandle: HWND; // Win32 API 类型
i: Integer; // 在 System 单元中介绍的类型标识
----------
7、文件头
建议在所有源文件、工程文件、单元文件等中使用信息化文件头。一个良好的文件头应包含以下信息:
{*******************************************************}
{ }
{ 软件名称 }
{ }
{ 版权所有 (C) 2012 公司名称 }
{ }
{*******************************************************}
----------
【命名规范】
----------
1、函数
函数名总是以大写字母开头,并且总是采用大小写结合的方式,每个单词的开头采用大写,其余采用小写。例如:
procedure thisisapoorlyformattedroutinename;
应改成:
procedure ThisIsMuchMoreReadableRoutineName;
函数名应该同它的内容相符,一个会导致某个行为的函数应以动词开头。一个用于设置输入参数的函数应以单词 Set 作为前缀,一个用来接收某个值的函数应以单词 Get 作为前缀。例如:
procedure CreateForm(...); { Create 动词 }
procedure SetUserName(...); { Set 设置 }
function GetUserName: string; { Get 获取 }
用正确的“反义词组”命名具有互斥意义的变量或相反动作的函数等。下面是一些在软件中常用的反义词组。
set get
add remove
begin end
create destroy
insert delete
first last
get release
increment decrement
put get
add delete
lock unlock
open close
min max
old new
start stop
next previous
source target
show hide
send receive
source destination
cut paste
up down
在函数或过程的前面填写适当的头信息,例如:
{-------------------------------------------------------------------------------
创建: 创建信息,作者,日期
名称: FunctionName
功能: 本函数或过程的主要功能
参数: Param1:...
Param2:(out)...
返回值: 类型、值
说明: 备注信息、调用方法、初始化数值等
-------------------------------------------------------------------------------}
----------
2、函数参数
所有函数的参数名称应该符合它们所代表的意义,特别是应该以传送到函数中的标志符的名称为基础。
在类的方法中,如果一个参数的名称容易和“类中的属性名称”或“系统关键字”相混淆时,则应该在参数名称前添加“A”前缀。“A”前缀按约定表示该参数的名称是与类类型中的一个属性或字段(域)的名称相对应。例如:
TCustomForm.Create(AOwner: TComponent);
TCustomForm.SetParent(AParent: TWinControl);
TCustomForm.SetDesigner(ADesigner: IDesignerHook);
TCustomForm.AlignControls(AControl: TControl; var Rect: TRect);
TCustomForm.RecreateAsPopup(AWindowHandle: HWND);
----------
3、变量
变量名一般以代表该变量类型的小写字母为前缀,常用类型的前缀列表如下:
i : Integer;
f : Single, Double;
c : Char;
p : Pointer;
b : Boolean;
uc : Byte;(UnsignedChar)
w : Word;
dw : LongWord;(DWord)
a : Array, Array of TYPE;
s : String;
以上前缀可以进一步组合成新的类型,自定义的数据类型可以自己规定类型前缀,如果该类型使用较为广泛,可以升级为公司内部的通用前缀。目前已有的内部通用前缀为:
pid : PID 变量
变量名各单词之间以大写字母分隔。例如:
susername
应改成
sUserName
变量的名称应与使用它们的目的相符。循环控制变量应采用一个单独的字符作为名称,比如 I、J、K,也可以采用更加有意义的名称,比如 UserIndex。逻辑变量的名称应能充分表达准确的真或假的意思。临时变量的取名应合理。
局部变量用于函数内部,遵循其他变量的命名规则。局部变量加前缀 l_(循环控制变量除外),如 l_UserName。
----------
4、常量(资源字符串)
常量(资源字符串)的命名应当能够表达出它的用途。如果常量(资源字符串)有类别,则该类别的所有常量必须加相同的前缀,前缀为该类别缩写的小写字母。例如:对于文件打开方式定义的常量:
fmOpenRead = $0000;
fmOpenWrite = $0001;
如果常量没有类别,则常量的默认前缀为 con。例如:
conProductName = '示例程序';
如果资源字符串没有类别,则资源字符串的默认前缀为 rs。例如:
rsFileOpenError = '文件打开失败!';
----------
5、标识符
标识符的命名应当符合“min-length && max-information”规则。
较短的单词可通过去掉“元音”形成缩写,较长的单词可取单词的头几个字母形成缩写,一些单词有大家公认的缩写,常用单词的缩写必须统一。协议中的单词的缩写与协议保持一致。对于某个系统使用的专用缩写应该在某处做统一说明。
----------
6、枚举类型
枚举类型的名称应符合使用它们的目的。该类型的声明应以字符 T 为前缀,以表明这是一个类型。枚举类型中的元素的前缀应包含 2~3 个小写字符来彼此关联。例如:
type
TSongType = (stRock, stClassical, stCountry, stAlternative, stHeavyMeta1, stRB);
一个枚举类型的实例名称应与不要前缀“T”的枚举类型相同,例如:
var
SongType: TSongType;
如果有更好的原因,则可以赋予该变量更特殊的名称,例如:
var
FavoriteSongType1: TSongType;
FavoriteSongType2: TSongType;
----------
7、数组类型
数组类型的名称应符合使用它们的目的。该类型的声明应以字符 T 为前缀,以表明这是一个类型。如果要声明该数组类型的指针类型,那么指针类型应以 P 为前缀,并且应立即声明在该数组声明的前面。例如:
type
PCycleArray = ^TCycleArray;
TCycleArray = array[1..100] of Integer;
----------
8、记录类型
记录类型的名称应符合使用它们的目的。该类型的声明应以字符 T 为前缀,以表明这是一个类型。如果要声明该记录类型的指针类型,则指针类型应以字母 P 为前缀,并且应立即声明在该记录声明的前面。例如:
type
PEmployee = ^TEmployee;
TEmployee = record
EmployeeName: string;
EmployeeRate: Double;
end;
----------
9、类类型
类类型的名称应符合使用它们的目的。该类型的声明应以字符 T 为前缀,以表明这是一个类型。例如:
type
TMyObject = class(TObject);
一个类类型的实例名称应与不要前缀“T”的类类型相同,例如:
var
MyObject: TMyObject;
类的字段(域)名遵循与变量标识符同样的约定,只不过要以 F 为前缀,以表明这是一个字段(域)的名称。所有的字段(域)都必须是私有的。想在类的范围之外存取字段(域)值,必须通过属性来完成。
方法的命名应遵循本文档中有关过程和函数的约定叙述。
属性如果是表示为一个私有字段(域)的存取器的话,那么它的名称应是它们所操作的字段(域)的名称去掉前缀“F”。属性的名称应是名词,不是动词。属性表示的是数据,而方法表示的是行为。数组类型的名称应为复数。一般情况下属性的名称应为单数。
----------
10、项目文件
Delphi XE 已经支持中文项目文件名了,这个可以根据自己的需要来自由命名。
----------
11、单元文件
Form 文件的名称应当表达出 Form 的用途,且具有 Frm 后缀。例如,About 的文件名叫 AboutFrm,主 Form 的文件名叫 MainFrm。
数据模块的文件名应当表达出数据模块的作用,且具有 DM 后缀。例如,Customers 数据模块的文件名叫 CustomersDM.dfm。
远程数据模块的文件名应当表达出远程数据模块的用途,且具有 RDM 后缀。例如,Customers 远程数据模块的文件名叫 CustomersRDM.dfm。
通用单元的名称应当表达出它的用途。例如,一个实用工具单元的名称叫 ugUtilities.pas,包含全局变量的单元名称叫 CustomerGlobals.pas。一个项目中单元名称必须是唯一的。
----------
12、包
包的名称应依照下面的例子:
“iiilibvv.pkg” - 设计时刻包
“iiistdvv.pkg” - 运行时刻包
字符“iii”表示一个 3 字符标识前缀。这个前缀用来表明公司、个人或其它有标识意义的实体。字符“vv”表示为该包所对应的 Delphi 的版本号。包的名称中包含“lib”或“std”的意思是表明这是一个设计时刻包还是一个运行时刻包。
----------
13、组件
CnPack 插件会自动为“我们所添加的组件”修改前缀信息,我们遵照 CnPack 的默认设置即可。由于 Delphi 的组件在不断的增加,所以,这里只列出部分常用组件的前缀信息(这里的前缀信息可能与 CnPack 不一致,仅供参考)。
Standard 页
mm TMainMenu
pm TPopupMenu
mmi TMainMenuItem
pmi TPopupMenuItem
fm TFrame
lbl TLabel
edt TEdit
mem TMemo
btn TButton
ck TCheckBox
rb TRadioButton
lb TListBox
cb TComboBox
scb TScrollBar
gb TGroupBox
rg TRadioGroup
pnl TPanel
al TActionList
ac TAction
Additional 页
bbtn TBitBtn
sb TSpeedButton
me TMaskEdit
sg TStringGrid
dg TDrawGrid
img TImage
shp TShape
bvl TBevel
sbx TScrollBox
clb TCheckListBox
spl TSplitter
stx TStaticText
ctrb TControlBar
av TApplicationEvents
cht TChart
Win32 页
tbc TTabControl
pgc TPageControl
il TImageList
re TRichEdit
tbr TTrackBar
pb TProgressBar
ud TUpDown
hk THotKey
ani TAnimate
dtp TDateTimePicker
mc TMonthCalendar
tv TTreeView
lv TListView
hc THeaderControl
stb TStatusBar
tb TToolBar
clb TCoolBar
pgs TPageScroller
System 页
tm TTimer
pb TPaintBox
mp TMediaPlayer
olec TOleContainer
ddcc TDDEClientConv
ddci TDDEClientItem
ddsc TDDEServerConv
ddsi TDDEServerItem
Data Access 页
ds TDataSource
tbl TTable
qry TQuery
sp TStoreProce
db TDataBase
ssn TSession
bm TBatchMove
usql TUpdateSQL
nt TNestedTable
Data Controls 页
dbg TDBGrid
dbn TDBNavigator
dbt TDBText
dbe TDBEdit
dbm TDBMemo
dbi TDBImage
dblb TDBListBox
dbcb TDBComboBox
dbck TDBCheckBox
dbrg TDBRadioGroup
dbll TDBLookupListBox
dblc TDBLookupComboBox
dbre TDBRichEdit
dbcg TDBCtrlGrid
dbch TDBChart
ADO 页
adon TADOConnection
adoc TADOCommand
adod TADODataSet
adot TADOTable
adoq TADOQuery
ados TADOStoreProc
rdsn TRDSConnection
Midas 页
prv TProvider
cds TClientDataSet
qcds TQueryClientDataSet
dcom TDCOMConnection
olee TOleEnterpriseConnection
sck TSocketConnection
rms TRemoteServer
mid TMidasConnection
Internet 页
csk TClientSocket
ssk TServerSocket
wbd TWebDispatcher
pp TPageProducer
tp TQueryTableProducer
dstp TDataSetTableProducer
nmdt TNMDayTime
nec TNMEcho
nf TNMFinger
nftp TNMFtp
nhttp TNMHttp
nMsg TNMMsg
nntp TNMNNTP
npop TNMPop3
nuup TNMUUProcessor
smtp TNMSMTP
nst TNMStrm
nsts TNMStrmServ
ntm TNMTime
nudp TNMUdp
psk TPowerSock
ngs TNMGeneralServer
html THtml
url TNMUrl
sml TSimpleMail
Decision Cube 页
dcb TDecisionCube
dcq TDecisionQuery
dcs TDecisionSource
dcp TDecisionPivot
dcg TDecisionGrid
dcgr TDecisionGraph
QReport 页
qr TQuickReport
qrsd TQRSubDetail
qrb TQRBand
qrcb TQRChildBand
rqg TQRGroup
qrl TQRLabel
qrt TQRText
qre TQRExpr
qrs TQRSysData
qrm TQRMemo
qrrt TQRRichText
qrdr TQRDBRichText
qrsh TQRShape
qri TQRImage
qrdi TQRDBMImage
qrcr TQRCompositeReport
qrp TQRPreview
qrch TQRChart
Dialogs 页
对话框元件实际是以元件形式封装的 Form,因此它遵循Form的命名规则。其类型经由元件的名称定义了。实例的名称与类型的名称相同,但没有前缀T。
OpenDialog TOpenDialog
SaveDialog TSaveDialog
OpenPicturedialog TOpenPictureDialog
SavePictureDialog TSavePictureDialog
FonDialog TFontDialog
ColorDialog TColorDialog
PrintDialog TPrintDialog
PrintSetupDialog TPrinterSetupDialog
FindDialog TFindDialog
ReplaceDialog TReplaceDialog
Win31 页
dbll TDBLookupList
dblc TDBLookupCombo
ts TTabSet
ol TOutline
tnb TTabbedNoteBook
nb TNoteBook
hdr THeader
flb TFileListBox
dlb TDirectoryListBox
dcb TDirveComboBox
fcb TfilterComboBox
Samples 页
gg TGauge
cg TColorGrid
spb TSpinButton
spe TSpinEdit
dol TDirectoryOutline
cal TCalendear
ibea TIBEventAlerter
ActiveX 页
cfx TChartFx
vsp TVSSpell
flb TF1Book
vtc TVTChart
----------
【编程习惯】
----------
1、函数
函数的功能要单一,不要设计多用途的函数。多用途的函数往往通过在输入参数中有一个控制参数,根据不同的控制参数产生不同的功能。这种方式增加了函数之间的控制耦合性,而且在函数调用的时候,调用相同的一个函数却产生不同的效果,降低了代码的可读性,也不利于代码调试和维护。
源文件中必须有被调用函数的显式的函数原型说明。
函数尽量只有一个出口(Return),避免从循环语句中引出多个出口(Exit)。
----------
2、函数参数
在定义函数时,如果函数有参数的话,相同类型的参数一般要求放在一起。例如:
procedure Foo(Param1, Param2: Integer; Param3: Integer; Param4: string);
应改成:
procedure Foo(Param1, Param2, Param3: Integer; Param4: string);
最常用的参数应当作为第一个参数,按使用频率依次从左到右排。输入参数位于输出参数之前。将通用的参数放在特殊参数的前面。范围大的参数应当放在范围小的参数之前。排序有可能有些例外,比如事件的处理,类型为 TObject 的 Sender 参数经常放在第一位。
当一个参数为 Record、Array、ShortString、或 Interface 并且在函数中不被改变时,应将其标识为 const 类型。这将提高程序的效率。其他类型的参数也提倡这样做,虽然不能提高程序的效率,但有助于调用者对参数的理解。
调用函数时,全局变量不能作为函数的参数。
----------
3、变量
如果必须的话,在刚进入函数就应初始化局部变量。
指针类型变量必须初始化为 NULL。
----------
4、常量
源文件里不能出现无意义的数字,所有数字都用常量代替。常量定义时应该有相应说明。
如果在模块中有提示信息之类的字符串,则应把该字符串定义为资源字符串。
----------
5、名称的冲突
当两个单元中存在名称相同的函数时,如果你调用该函数,在 uses 子句中排在后面的单元中的函数将会被调用。为了解决这个问题,要在调用该函数时写上相应的单元前缀。比如:
SysUtils.FindClose(SR);
Windows.FindClose(Handle);
----------
6、全局变量
使用全局变量是不推荐的。但是,在某些时候还是必须使用,而且它们也只应在必须使用的时候才使用。在这种时候,你应努力只在一段上下文范围内使用全局变量。例如,一个全局变量只应在一个单元的 implemntation 部分内是全局的。如果全局变量只对某个对象有用,比如 Form1,则应该将其声明为 TForm1 的数据成员,以便提高效率和统一管理。如果打算在多个单元内使用全局数据,你应将它们移到一个单独的公共单元中,然后被其它所有单元使用。
全局变量可以在 var 子句中直接初始化为一个值。记住,所有的全局数据会自动初始化为 0,因此不要将全局变量初始化为一个“空”值比如 0、nil、''、Unassigned 等等。这样做的原因是因为未初始化的全局数据在 exe 文件中不会占据任何空间。未初始化的数据被存储在一个虚拟的数据段,它在应用程序启动后被分配在一段内存中,而初始化的全局数据则在硬盘的 exe 文件中占用空间。
----------
7、浮点类型
不推荐使用 Real 类型,因为它的存在只是为了向前兼容早期的 Pascal 代码。在通常情况下对于浮点数应当使用 Double 类型。Double 对处理器和总线而言是做了最优化处理的,它也是 IEEE 中定义的标准数据格式。只有当需要的范围超出 Double 所定义的范围时才使用 Extended。Extended 是 Intel 定义的类型且在 Java 中不支持。只有当浮点变量的实际字节大小有其意义时(比如当使用另一种语言编写的 DLL 时)才使用 Single。
----------
8、变体类型
通常不建议使用 Variant 和 OleVariant 类型。但在只有运行时刻才能知道数据类型的程序中,则必须使用这两个类型,这种情形多出现在 COM 和数据库开发中。OleVariant 使用在以 COM 为基础的编程中,例如自动化和 ActiveX 控制,而 Variant 则使用在非 COM 的编程中,这是因为 Variant 能够有效地保存 Delphi 的原生字符串,而 OleVariant 会将所有的字符串转换为 Ole 字符串(即 WideChar 字符串)且没有引用计数功能,它们永远通过拷贝来传递。
----------
9、类类型
当你不希望一个方法被派生类重载时,则应当使用“静态方法”。
当你希望一个方法能被派生类重载,则应当使用“虚拟方法”。如果类的方法将被多个派生类直接或间接地使用,则应当将其定义为“动态方法”。例如,某一个类含有一个被频繁重载的方法,并且有 100 多个派生类,则应将该方法定义为“动态方法”,因为“动态方法”可以减少内存的开销,而“虚拟方法”会消耗大量内存。
“抽象方法”只能在那些从不允许创建实例的“抽象类”中使用。Delphi 不提倡使用“抽象方法”,而提倡使用“空方法”来代替“抽象方法”。例如:
constructor TObject.Create; virtual; abstract; { 不提倡 }
constructor TObject.Create; virtual; { 提倡,这个方法什么也没做,它的作用和虚拟方法类似 }
begin
end;
所有存取类的方法都只能出现在类的 private 或 protected 部分。属性存取方法的命名应遵循函数和函数的约定规则。“属性读取方法”必需以单词 Get 为前缀。“属性写入方法”必需以单词 Set 为前缀。“属性写入方法”的参数名称应为 Value,并且它的类型应是它所操作的属性的类型。
尽管不是必须,但还是建议您使用“属性写入方法”来访问代表私有字段的特性。
----------
10、if 语句
在 if ... then ... else 语句中最常发生的行为应放在 then 子句中,而其它发生可能性较小的行为应放在 else 子句中。
尽量避免使用嵌套的 if 语句,在这种情形下应使用多个 if 语句来判断各种可能。
不要使 if 嵌套超过 5 层。应该用其它方法使代码更加清晰、明了。
不要在 if 语句中使用不必要的括号。
如果在 if 语句中有多个条件需测试,这些条件应按计算强度由少到多的顺序从左到右排列。这样做能使编译器在编译代码时获得布尔估算逻辑的捷径,从而使你的代码获得最佳的优化。
----------
11、case 语句
在一个 case 语句中各个独立单元的情况常量应以数字或字母顺序排列。
每一个 case 单元的动作行为应保持简单而不应该超过四到五行代码。如果所要执行的动作过于复杂,则应采用独立的过程或函数。
case 语句中的 else 子句只有当需要缺省行为或处理错误时才使用。
case 语句应遵循其它结构的缩进和命名约定。
----------
12、while 语句
在一个 while 语句中不建议使用 Exit 来跳出循环,尽量仅使用循环条件来跳出循环,必要的时候可以使用 Break 跳出循环。
在一个 while 循环中所需要的初始化工作应位于 while 循环前面,而且不要被其它无关的语句隔开。
任何结束后的处理应在循环之后立即进行。
----------
13、for 语句
只有当循环次数已知的情况下才能使用用 for 语句取代 while 语句。
----------
14、repeat 语句
repeat 语句的使用同 while 语句一样,并且遵循同样的通用方针。
----------
15、with 语句
with 语句应小心使用,避免过度使用 with 语句,尤其在 with 语句中要小心使用多个对象或记录,以免使程序员感到困惑并难以发现问题所在。例如:
with Record1, Record2 do
with 语句遵循本文档所说明的命名约定和缩进的格式规则。
----------
16、包
运行时刻的包应只包含“其它构件包所要求的单元或构件”。另外,包含“属性、构件编辑器和其它只为设计的代码”应放入到设计时刻包中。注册单元应放在设计包中。
----------
17、结构异常处理
异常处理大量使用在错误纠正和资源保护方面。这就是说一旦资源被分配,必需用一个 try ... finally 来保证该资源被正确的释放。这种异常的保护也是指在一个单元的 initializition、finalization 或一个对象的 constructor、destructor 中进行资源的分配和释放。
try ... finally 的使用
任何情形下,每一次的分配都应跟随一个 try ... finally。
try ... except 的使用
只有当“在异常被触发而你想执行一些任务”时才使用 try ... except。通常,你没有必要为了只是简单地在屏幕上显示一个错误信息而使用 try ... except 语句,因为这会被 Application 对象自动执行。如果你想在 except 子句中执行完一些任务之后调用缺省的异常处理,使用 raise 来重新触发异常到下一个句柄。
try ... except ... else 的使用
try ... except 中的 else 子句不建议使用,因为它会打断所有的异常,包括那些你没有准备的异常。
----------
18、代码调试
可以使用“条件编译语句”帮助调试:
{$IFDEF DEBUG}
i := 100;
ShowMessage(IntToStr(i));
{$ENDIF}
也可以使用“消息编译语句”帮助调试:
{$MESSAGE '提示,编译会通过'}
{$MESSAGE HINT '提示,编译会通过'}
{$MESSAGE WARN '警告,编译会通过'}
{$MESSAGE ERROR '错误,编译不会通过'}
{$MESSAGE FATAL '致命错误,编译不会通过'}
这样在编译过程中,会在 Delphi 的输出窗口中输出指定的文本,双击输出的文本,可以快速定位到语句行。其中前两种方法作用相同。
也可以使用“断言”帮助调试:
Assert(List = nil, '长度不匹配!');
建议广泛采用断言,以增强程序的调试功能。断言是一种用于调试的条件判断,它表示程序执行到某一点时必须满足的条件,若条件不满足则引发 EAssertionFailed 异常。在调试环境时,设置编译参数 Assetions 为 On,正式版本中设置 Assertions 为 Off 。
也可以使用“调试输出语句”帮助调试:
OutputDebugString(PChar(S));
OutputDebugString('输出提示信息');
这样在调试运行阶段,执行到上面的代码后,会在 Delphi 的输出窗口中输出指定的信息。
调试 Delphi 自身的单元文件的方法:
如果要调试 Delphi 自身的单元文件,可以在工程选项中设置:Project -> Option -> Delphi Compiler -> Compiling -> Debugging -> Use debug .dcus(勾选即可)
如果只想调试部分 Delphi 单元文件,可以将需要调试的 Delphi 单元文件添加到工程中来,这样在调试时,就可以对指定的单元文件进行调试,而不会跳转到其它 Delphi 单元文件。
跟踪代码的方法:
下断点时下在 begin 位置(而不是 begin 之后的代码位置),然后 F7 单步进入,这样可以捕获进入过程前的初始化部分,准确跟踪代码走向。还可以通过调用堆栈(Call Stack)窗口来查看代码的走向。
----------
19、可读性
编程首先考虑的是满足正确性、健壮性、可维护性、可移植性等质量因素,最后才考虑程序的效率和资源占用。一条语句只完成一个功能。复杂的语句阅读起来,难于理解,并容易隐含错误。变量定义时,一行只定义一个变量。保持代码的简单化是软件工程化的基本要求,不要过分追求技巧,否则会降低程序的可读性。
----------