来自:掱柟 > 馆藏分类
配色: 字号:
代码书写规范
2016-02-20 | 阅:  转:  |  分享 
  
2.1缩格

???缩格是指在每一级有两个空格。不要在源代码中保留tab字符,这是因为tab字符会随着不同用户的不同设置和不同的资源管理工具(打印、文档、版本控制等)而代表不同的宽度。

???你可以通过关闭Environment选项对话框中Editor页上的“Usetabcharacter”和“Optimalfill”检查框(通过Tools|Environment)来禁止保存tab字符。

2.2页边空格

页边空格会被设置成80字符宽。通常,源码不会超出这个边界,但这个方针会有一些弹性。不管是否有可能,那些超出到另一行的语句会在一个逗号或其他操作符之后与前面的语句相连。当一个语句被打断相连时,它应比原来的那一行语句缩进两个字符。

2.3Begin...End配对

Begin子句应写在独立的一行。例如,下面第一行是错误的写法而第二行是正确的。forI:=0to10dobegin//错误,begin同for在同一行forI:=0to10do//正确,begin出现在独立的一行begin

这个规则的例外是当begin子句的出现是作为一个else子句的一部分-参考例子:ifsomestatementthenbegin...endelsebeginsomeOtherStatement;end;

end语句永远出现在独立的一行。当begin语句不是一个else子句的一部分时,相应的end语句永远缩进到与begin部分相对应的位置。

三、OBJECTPASCAL

3.1括号

永远不要在括号与括号之间的字符中间留下空格。下面的例子示范了错误的与正确地使用括号中的空格:CallProc(Aparameter);//错误CallProc(Aparameter);//正确

永远不要在一个语句中使用不必要的括号。括号只应在源代码中需要的地方使用。以下的例子示范了错误和正确的使用:if(I=42)then//错误-多余的括号if(I=42)or(J=42)then//正确-需要括号

3.2保留字和关键字

ObjectPascal保留字和关键字永远是全部小写。

3.3过程和函数

3.3.1命名/格式化

例程的名字永远应该以大写的字母开头并且中间错落分明以便于可读性。下面是一个不正确格式的过程名称:procedurethisisapoorlyformattedroutinename;

下面是一个合适的大小写例程名称的例子:procedureThisIsMuchMoreReadableRoutineName;

例程的名称应该同它的内容相符。一个会导致某个行为的例程应以动词开头。例如:procedureFormatHardDrive;

一个用于设置输入参数的例程应以单词set作为前缀,例如:procedureSetUserName;

一个用来接收某个值的例程应以单词get作为前缀,例如:procedureGetUserName:string;

3.3.2形式参数

a.格式化

如果有的话,相同类型的形参应合并在一个语句中:procedureFoo(Param1,Param2,Param3:Integer;Param4:string);

b.命名

所有形参的名字应是十分符合它们所代表的意义,特别是应该以传送到例程中的标志符的名称为基础。一个好的参数名称应以字符A为前缀-例如:procedureSomeProc(AuserName:string;AuserAge:integer);

“A”前缀按约定表示该参数的名称是与类类型中的一个属性或域的名称相对应的。

c.参数的排序

下面的形参的顺序重点说明了注册者调用约定调用的好处。

-最常用的参数应放在第一位,其它的参数应按从左到右的顺序排列。-输入参数列表应放在输出参数列表的左边。-将通用的参数放在特殊参数的左边,例如:procedureSomeProc(Aplanet,AContinent,Acountry,Astate,Acity)-排序有可能有些例外,比如事件的处理。类型为TObject的Sender参数经常放在第一位。

d.常量参数

当一个参数为记录型、数组类型、ShortString、或接口类型并且在例程中不被改变时,这些参数应做上常量标记。这样做会让编译器更加有效率的产生有关这些不改变的参数的代码。

而例程中另外一些非变参数也可常量来传送。尽管这样做没有产生任何效果和提高效率,这将会给调用例程的使用者提供更多的信息。

e.名称的冲突

当使用拥有两个名称相同的例程的两个单元时,如果你调用该例程时,在uses子句中排在后面的单元中的例程将会被调用。为了解决这种“在uses子句上的模糊”冲突,要在调用该例程时写上相关的单元的前缀,例如:sysUtile.FindClose(SR);或windows.FindClose(Handle);

3.4变量

3.4.1变量的命名和格式

变量的命名应以使用它们的目的相符。循环控制变量应采用一个单独的字符作为名字,比如I,J,或K,也可以采用更加有意义的名字,比如UserIndex。逻辑变量的名字应能充分表达准确的真或假的意思。

3.4.2局部变量

一个过程中的局部变量应遵循所有其它变量的使用和命名约定。临时变量的取名应合理。

如果必须的话,在一进入例程就应初始化局部变量。局部的AnsiString变量会自动初始化为一个空的字符串。局部接口和派分接口类型变量将会自动初始化为nil,并且局部变数和ole变数类型变量会自动初始化为Unassigned

3.4.3全局变量的使用

使用全局变量是不推荐的。但是,在某些时候还是必须使用,而且它们也只应在必须使用的时候才使用。在这种时候,你应努力只在一段上下文范围内使用全局变量。例如,一个全局变量只应在一个单元的implemntation部分内是全局的。如果打算在多个单元类使用全局数据,你应将它们移到一个公共的单元中然后被其它所有单元使用。

全局变量可以在var子句中直接初始化为一个值。记住,所有的全局数据会自动初始化为0,因此不要将全局变量初始化为一个“空”值比如0、nil、''''、Unassigned、等等。这样做的一个理由是因为零-初始化的全局数据在exe文件中不会占据任何空间。零-初始化数据被存储在一个虚拟的数据段,它在应用程序启动后被分配在一段内存中。非零-初始化的全局数据在硬盘的exe文件占用空间。

3.5类型

3.5.1大写约定

如果类型的名字是保留字,那么它应全部小写。Win32API类型通常全部大写,并且你必须遵循在Windows.pas或其他API单元中的详细类型名称的约定。对于其他变量名字,地一个字母应为大写,而其他字母应错落有致。下面是一些例子:varMyString:string;//保留字WindowHandle:HWND;//Win32API类型I:Integer;//在System单元中引进的类型标识符

a.浮点指针类型

不推荐使用Real类型,因为它的存在只是为了向前兼容早期的Pascal代码。在通常情况下用Double来实现浮点指针的需要。并且,Double对处理器和总线而言是做了最优化处理的,它也是IEEE中定义的标准数据格式。只有当需要的范围超出Double所定义的范围时才使用Extended。Extended是intel定义的类型且在Java中不支持。只有当浮点指针变量的实际字节大小有其意义时才使用Single。(比如当使用另一种语言的DLLs时)。

b.枚举类型

枚举类型的名字需符合使用该类型的目的。该类型的名字需以字符T为前缀,以表明这是一个类型。枚举类型中的标识符列表必须包含两个或三个字符的前缀来对应于该枚举类型的名字-例如:TsongType=(stRock,stClassical,stCountry,stAlternative,stHeavyMetal,stRB);一个枚举类型的实例的名字应与不要前缀的枚举类型(SongType)相同,除非有更好的原因来赋予该变量更特殊的名字,比如:FavoriteSongType1,FavoriteSongType2等等。

c.变数和ole变数类型

通常不建议使用变数和Ole变数类型。但在只有运行时刻才能知道数据类型的程序中必须使用该类型,这种情形多出现在COM和数据库开发中。Ole变数使用在以COM为基础的编程中例如自动化和ActiveX控制,而变数使用在非COM的编程中,这是因为变数可以十分有效地存储本地Delphi字符串(同一个字符串变量一样),但Ole变数会将所有的字符串转换为Ole字符串(WideChar字符串)并且并不实例运算-它们永远拷贝。

3.5.2结构类型

a.数组类型

数组类型的名字需符合它们使用的目的。该类型的名字必须加以前缀T。如果须声明该数组类型的指针,那么该指针需加以前缀P而且应立即声明在该数组声明的前面。例如:typePCycleArray=^TCycleArray;TCycleArray=array[1...100]ofinteger;在实际应用中,数组的变量实例的名称应是其类型的名字去掉前缀T。

b.记录类型

记录类型的名字应符合使用它们的目的。其类型的声明应加以前缀T。如果要声明该记录类型的指针,就应加以前缀P并且应紧靠在类型声明的前面声明。例如:typePEmployee=^TEmployee;TEmployee=recordEmployeeName:string;EmployeeRate:Double;end;

3.6语句

3.6.1if语句

在if/then/else语句中最常发生的行为应放在then子句中,而其它发生可能性较小的行为应放在else子句中。

尽量避免使用嵌套的if语句,在这种情形下应用多个if语句来判断各种可能。

不要使用if嵌套超过五级深度。应使代码编写得更加清晰、明了。

不要在if语句中使用不必要的圆括号。

如果在if语句中有多个条件需测试,这些条件应按计算强度由少到多的顺序从左到右排列。这样做能使编译器在编译代码时获得布尔估算逻辑的捷径,从而使你的代码获得最佳的优化。举例来说,如果条件1快过条件2,而条件2快过条件3,那么在if语句中的排列应是:if条件1and条件2and条件3then

3.6.2case语句

在一个case语句中的各个独立的单元应以数字或字母顺序排列。

每一个case单元的动作行为应保持简单而不应该超过四到五行代码。如果所要执行的动作过于复杂应采用独立的过程或函数。

Case语句中的else子句只有当需要缺省行为或处理错误时才使用。

case语句应遵循其它结构的缩格和命名约定。

3.6.3while语句

在一个while语句中不建议使用exit过程来跳出循环,尽量仅使用循环条件来跳出循环。

在一个while循环中所用的初始化代码应紧靠在进入while循环前面出现而不要被其它不相关的语句隔开。

任何结束后的处理应在循环之后立即进行。

3.6.4for语句

for语句只有当循环次数已知的情况下才能取代while语句使用。

3.6.5repeat语句

repeat语句的使用同while语句一样,并且遵循同样的通用方针。

3.6.6with语句

with语句应节省使用,并且带有大量的警告。避免过度使用with语句并且在with语句中小心使用多个对象、记录等等。例如:withRecord1,Record2do这些事情会使程序员感到困惑并难以发现问题所在。

with语句遵循本文档所说明的命名约定和缩格的格式规则。

3.7结构异常处理

3.7.1一般话题

异常的处理大量地使用在错误纠正和资源保护方面。这就是说一旦资源被分配,一个try...finally必需加以使用来保证该资源被正确的释放。这种异常的保护也是指在一个单元的initializition/finalization或一个对象的constructor/destructor中进行资源的分配和释放。

3.7.2try...finally的使用

任何情形下,每一次的分配都应跟随一个try...finally。举例来说,下面的代码会造成可能的错误:SomeClass1:=TsomeClass.Create;SomeClass2;=TsomeClass.Create;try{dosomecode}finallySomeClass1.Free;SomeClass2.Free;end;

一个更安全更合适的分配过程应是:SomeClass1:=TSomeClass.Create;trySomeClass2:=TsomeClass.Create;try{dosomecode}finallySomeClass2.Free;end;finallySomeClass1.Free;end;

3.7.3try...except的使用

只有当在异常被触发而你想执行一些任务时才使用try...except。通常,你没有必要为了只是简单地在屏幕上显示一个错误信息而使用try...except语句,因为这会被Application对象自动执行。如果你想在except子句中执行完一些任务之后调用缺省的异常处理,使用raise来重新触发异常到下一个句柄。

3.7.4try...except...else的使用

try...except中的else子句不建议使用,因为它会打断所有的异常包括那些你没有准备的异常。

3.8类类型

3.8.1命名和格式

类类型的名称应符合使用它们的目的。类型名字应加以前缀T以表明这是一个类型的定义-例如:typeTcustomer=class(TObject)类型的实例通常是没有前缀T的类型的名字-例如:varCustomer:Tcustomer;注意:查阅“构件类型的命名标准”来获得更多有关构件命名的信息。

3.8.2域

a.命名/格式

类的域名遵循与变量标识符同样的约定除了它们应以F为前缀,来表明这是一个域的名称。

b.可视化

所有的域都必需是私有的。想在类的范围之外存取域得通过属性来使用。

3.8.3方法

a.命名/格式

方法的命名应遵循本文档中有关过程和函数的约定叙述。

b.使用静态的方法

如果使用一个静态的方法,那么该方法就不能被该类的后代类所继承。

c.使用虚拟/动态的方法

如果你打算该类的方法能被后代的类所继承就得使用虚拟的方法。只有在该方法有多个继承时(直接的或间接的)才使用动态的方法。例如,一个类类型包含一个可继承的方法,而100个后代类要继承这种方法,那么这个方法就会动态地产生为100个后代类使用的内存。

d.使用抽象的方法

如果在一个类中使用抽象的方法,该类就不能被创建。只有在那些永远不会被创建的类中使用抽象的方法。

e.属性存取方法

所有存取类的方法都只能出现在类的private或protected部分。属性存取方法的命名应遵循过程和函数的约定规则。读取存取方法(方法读取器)必需以单词Get为前缀。写入存取方法(方法写入器)必需以单词Set为前缀。方法写入器的参数的名字应为Value,并且它的类型应是它所操作的属性的类型。例如:TSomeClass=class(TObject)privateFsomeField:Integer;protectedfunctionGetSomeField:Integer;procedureSetSomeField(Value:Integer);publicpropertySomeField:IntegerreadGetSomeFieldwriteSetSomeField;end;

3.8.4属性

a.命名/格式

属性如果是表示为一个私有域的存取器的话,那么它的名字应是它们所操作的域的名字除去解释符F。

属性的名字应是名词,不是动词。属性表示的是数据,而方法表示的是行为。

数组类型的名称应为复数。一般情况下属性的名称应为单数。

b.使用存取的方法

尽管没有要求,但还是建议尽量少地为一个表示私有域的属性而使用写入存取方法。

??2003-5-1321:11:12???四、文件

4.1工程文件

工程文件应取个描述性的名字。例如,Delphi开发者指南错误管理器的工程名字是:DDGBugs.dpr。一个有关系统信息的程序的名字就应象SysInfo.dpr。

4.2窗体文件

一个窗体文件的取名应可以描述使用该窗体的目的,并加以后缀Frm。例如,一个“关于”的窗体的文件名应是AboutFrm.dpr。主窗体的文件名应是MainFrm.dpr。

4.3数据模板文件

数据模板的取名应能表示使用该数据模板的目的,它的名称应加以两个字符的后缀DM。例如,自定义数据模板的文件名字应为CustomersDM.dfm。

4.4远端数据模板文件

远端数据模板的取名应能表示使用该远端数据模板的目的,它的名称应加以三个字符的后缀RDM。例如,自定义远端数据模板的文件名字应为CustomersRDM.dfm。

4.5Unit文件

a.通用Unit结构

(i)unit的名字

Unit文件应取一个可描述性的名字。例如,包含应用程序主窗体的单元应叫做MainFrm.pas。

(ii)uses子句

在interface部分的uses子句应包含在interface部分中的代码所需要的单元。去掉那些Delphi可以自动加入到程序中的单元。

在implementation部分的uses子句应只包含在implementation部分中的代码所需要的单元的名字。去掉不必要的单元。

(iii)interface部分

interface部分应包含只那些其它单元所需要存取类型的定义、变量、过程/函数的预定义等等。否则,就应放在implementation部分定义。

(iv)implementation部分

implementation部分应包含那些只在本单元中私用的类型定义、变量、过程/函数定义等等。

(v)initialization部分

不要在initialization部分放入耗时长的代码,这将使程序的第一个界面出现得比较缓慢。

(vi)finalization部分

在这里要保证释放你在Initialization部分所分配的任何资源。

b.窗体单元

一个窗体的单元文件应拥有与它所对应的窗体文件同样的名称。例如,“关于”窗体的单元名称应为AboutFrm.pas,而主窗体的单元名称应为MainFrm.pas。

c.数据模板单元

一个数据模板的单元文件应拥有与它所对应的数据模板文件同样的名称。例如,一个自定义数据模板单元的名称应为CustomersDM.pas。

d.一般目的单元

一般目的单元的取名应符合使用该单元的目的。例如,一个实用程序单元取名为BugUtilities.pas。一个包含全局变量的单元取名为CustomerGlobals.pas。

注意,该单元的名字不能与它的工程中所使用的所有包中的单元的名字相同。不赞成使用一般的或通用的单元名字。

e.构件单元

构件单元应放在独立的目录,以将它们同定义构件组或构件集合的单元区分开来。它们要永远同工程在不同的目录。单元名字应同它们的内容相符。

注意:查阅“用户定义的构件”部分来获得更多有关构件命名标准的信息。

4.6文件头

建议在所有源文件、工程文件、单元等等中使用信息化文件头。一个良好的文件头应包含以下信息:{版权...著作的年、月、日...}

??2003-5-1321:12:40???五、窗体和数据模板

5.1窗体

a.窗体类型命名标准

窗体类型的取名应能表达使用该窗体的目的。类型定义应加以前缀T。前缀后面跟随着描述性的名字。最后,应加以Form后缀来描述名字。例如,一个“关于”的窗体的类型的名字应为:TAboutFrom=class(TForm);主窗体的定义为:TMainForm=class(TForm);一个用户接入窗体的名字应象:TCustomerEntryForm=class(TForm);

b.窗体实例命名标准

窗体实例应是没有带前缀T的相应类的名字。例如,对应于前面窗体类型而言,其实例的名字应为:

类型名称实例名称TAboutFormAboutFormTMainFormMainFormTCustomerEntryFormCustomerEntryForm

c.自动创建窗体

只有主窗体可以是自动创建的除非有其它更好的理由不这样做。所有其它的窗体必需从工程选项对话框中的自动创建列表中移走。查阅以下部分来获得更多的信息。

d.模式窗体实例化函数

所有的窗体单元都应包含一个窗体实例化函数,该函数用来创建、设置、模式地显示窗体,并释放窗体。该函数应返回窗体的模式结果。该函数要传递的参数应遵循本文档指定的“参数传递”标准。通过这种方式封装的函数性有助于代码的再利用和维护。

该窗体的变量要从单元中移走,并再窗体实例的函数中进行本地式地定义。注意,这就意味着该窗体必需从工程/选项对话框中的自动创建列表中剔除。参考本文档后面的“自动创建窗体”。

例如,下面的单元展示了再GetUserData窗体中的一个函数。unitUserDataFrm;interfaceuseswindows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Dialogs,StdCtrls;typeTUserDataForm=class(TForm)edtUserName:TEdit;edtUserID:TEdit;private{Privatedeclarations}public{Publicdeclarations}end;functionGetUserData(varaUserName:String;varaUserID:Integer):Word;implementation{$R.DFM}

functionGetUserData(varaUserName:String;varaUserID:Integer):word;varUserDataForm:TuserDataForm;beginUserDataForm:=TuserDataForm.Create(Application);tryUserDataForm.Caption:=''GettingUserData'';Result:=UserDataForm.ShowModal;if(Result=mrOK)thenbeginaUserName:=UserDataForm.edtUserName.Text;aUserID:=StrToInt(UserDataForm.edtUserID.Text);end;finallyUserDataForm.Free;end;end;end.

5.2数据模板

a.数据模板命名标准

数据模板的取名要符合使用该数据模板的目的。类型的定义应加以前缀T,后面紧接着描述性的名字,最后要加以后缀单词“DataModule”。例如,一个自定义的数据模板有时候应该象:TCustomerDataModule=class(TDataModule)一个命令式的数据模板的名字应象:TOrdersDataModule=class(TDataModule)

b.数据模板实例命名标准

数据模板实例的名称应是对应不带前缀T的类型的名称。例如,对于前面的窗体类型而言,其实例的名称应为:类型名称实例名称TCustomerDataModuleCustomerDataModuleTOrdersDataModuleOrdersDataModule

??2003-5-1321:13:47???六、包

6.1使用运行包和设计包的比较

运行时刻的包应只包含其它构件包所要求的单元或构件。另外,包含属性/构件编辑器和其它只为设计的代码应放入到设计时刻包中。注册单元应放在设计包中。

6.2文件命名标准

包的名称应依照下面的例子:“iiilibvv.pkg”-设计时刻包“iiistdvv.pkg”-运行时刻包字符“iii”表示一个3字符标识前缀。这个前缀用来表明公司、个人或其它有标识意义的实体。

字符“vv”表示为该包想要对应Delphi某个版本的包的版本号。

注意,包的名字中包含“lib”或“std”的意思是表明这是一个设计时刻包还是一个运行时刻包。

如果既是设计时刻包又是运行时刻包,该文件的命名是同上面一样的,例如,为Delphi开发者指南做的包的名称应为:

DdgLib40.pkg-设计时刻包DdgStd40.pkg-运行时刻包

??2003-5-1321:14:36???七、构件

7.1用户自定义构件

在标准构件中命名出来的构件的名称同在“类类型”部分定义中的一样定义成一个类类型,不同的是它们有一个3字符的指示前缀,这个前缀可以表示公司、个人或其它实体。例如,一个为Delphi开发者指南编写的时钟构件的名称定义为:TddgClock=class(TComponent)注意,那三个前缀字符是小写的。

7.2构件单元

构件单元应只包含一个主要的构件,一个主要的构件是指出现在构件栏中的构件。主要构件的辅助构件/对象应放入到同一个单元中。

7.3使用注册单元

构件的注册过程应从构件本身的单元中剔除,并放入到一个独立的单元中。这个注册单元可以用来注册任何构件、属性编辑器、构件编辑器、专家器等。

构件的注册只应在设计时刻包中进行,注册单元应包含在设计时刻包中而不应放在运行时刻包中。

推荐使用的注册单元的名称是:XxxReg.pas上面的3个前缀字符“Xxx”用来表示一个公司、个人或任何其它的实体。例如,在Delphi开发者指南中的注册单元的名称应为DdgReg.pas。

7.4构件实例命名约定

所有的构件都应取个描述性的名称。由Delphi创建的缺省名的构件不会被遗弃。在设计构件类型时应设计一个小写的前缀。使用前缀而不使用后缀的原因是在搜寻时,在对象检查器和代码探索器中搜寻构件的名字比搜寻构件的类型更容易实现。



献花(0)
+1
(本文系掱柟首藏)