Object Pascal Style Guide - By Charles Calvert
(Object Pascal Coding Style Guide - Translated by: Tommy Tong)
We acknowledge that many well-established studios or individuals have their own programming practices that differ from those described in this article, however, we strongly recommend that you use a tool to convert your code to Borland-style code, and then Submit to Borland, PRoject JEDI or any other public source code repository. We don't want to force you to change your habits, but we insist that all code that works with Borland products follow the habits described in this article.
Object Pascal is a beautiful design language. Strong readability is one of its advantages. The standards designed in this article will enhance the readability of Object Pascal code. When developers follow these simple habits demonstrated in this article, they will also become standards, which will benefit all Delphi developers using a unified and readable coding style. Efforts to enforce these standards will increase the value of source code for developers, especially during the maintenance and debugging cycle phases.
Although we believe in and admire the style promoted in this article, we are not necessarily endorsing it because it is right in itself and wrong in others. However, we believe that the standard followed by the vast majority of developers has its validity, so we still support and maintain this style. The human brain is always adapting to standards and finding ways to quickly organize familiar patterns so that their meaning can be understood quickly and efficiently. It is this requirement that establishes standards that will make it as easy as possible for a large number of people to read code. If our guidelines feel unfamiliar to you for the first time, then we ask you to stick with them for a while and you will find that you become accustomed to them. Or, if you prefer, you can keep your own style and convert it through a program that adheres to our standards, and then you can submit your code to Borland or other repositories.
Some text editors, like Visual SlickEdit, can help you format your code in a certain style.
A free formatter developed by Egbert van Nes is available at:
http://www.slm.wau.nl/wkao/delforexp.html
Another commercial program for Delphi is CrackerJax:
http://www.kineticsoftware.com/html/products.html
------------------------------------------------------
1.0 Introduction
This article is not an attempt to define syntax rules for the Object Pascal language. For example: it is illegal to put the seal ";" in front of else; the compiler does not allow this usage. So I won't show the grammar rules in this article. This article aims to define appropriate behavior where language provides choices. I usually stay silent where there is only one method of control.
1.1 Background
The guidelines presented in this article are based on part of the Delphi source code. Delphi source code follows exactly these guidelines. If you find a violation of these principles, then these principles, not the uncertain source code, should be your guideline. However, you can use the original code as a supplement to these principles, at least to help you get a general idea of the form of your own code.
1.2 Thanks
The formats in this article are based on work done to define style standards for the Java language. Java has no influence on the rules for formatting Object Pascal source code, but the documentation on Sun's website is the basis for this article. In some special places the style and format of this article were greatly inspired by "A Coding Style Guide for Java WorkShop and Java Studio Programming" (Achut Reddy, "Coding Guide for Java WorkShop and Java Studio"). The article can be found at this URL: http://www.sun.com/workshop/java/wp-coding
The Delphi team made a significant contribution to the completion of this article. In fact, without their help, this article would not have been completed.
2.0 Source files
Object Pascal source code is mainly divided into single source files and project files, and they all follow the same convention. Delphi project files have a .DPR extension. It is the main file of the project. Any unit file used in a project has a .PAS extension. Other files, such as batch files, HTML files or DLLs can also play a role in the project, but this article only covers project files and unit files.
2.1 Source file naming
Object Pascal supports long file names. If you are using several words to form a single name, it is best to use an initial capital letter for each word: MyFile.pas. This is considered parenthesis or camelCase. Extensions should be in lowercase. For historical reasons, Delphi source code often uses the 8:3 naming pattern, but developers do not have to be restricted by the above rules and turn to the Delphi team's usage.
If you are translating a C/C++ header file, then the Pascal file you translate should keep the same main file name as the C/C++ header file, and use .PAS as the extension. For example: Windows.h -> Windows.pas. If Pascal syntax forces you to combine several header files into a single unit file, then the file name of the header file that contains the other header files will be used as the name of the new unit file. For example: Windows.h contains the WinBase.h file, then the new unit file is named Windows.pas.
2.2 Source file organization
All Object Pascal unit files should contain the following elements in the following order:
Copyright/Identity Block Comments
unit name
interface segment
Implementation part
A terminator "end."
There should be at least one empty line between each section.
Other elements should be structured into whatever order you think is most appropriate. But the copyright should appear at the very beginning of the file, then the unit name, then any conditional definitions, compiler directives, or include statements, and then the uses clause:
{************************************************ ******}
{ }
{ Borland Delphi Visual Component Library }
{ }
{ Copyright (c) 1995,98 Inprise Corporation }
{ }
{************************************************ ******}
unit Buttons;
{$S-,W-,R-}
{$C PRELOAD}
interface
uses
Windows, Messages, Classes,
Controls, Forms, Graphics,
StdCtrls, ExtCtrls, CommCtrl;
If you put the type section before the const section, or mix the two, it has no effect.
The implementation part needs to write the implementation first, then the uses clause, and then other include declarations or other indicators:
implementation
uses
Consts, SysUtils, ActnList,
ImgList;
{$R BUTTONS.RES}
2.2.1 Copyright/Identity Block Comments
Every source file should begin with a block comment containing version information and a standard copyright notice. Version information can look like this:
{************************************************ ******}
{ }
{ Widgets Galore }
{ }
{ Copyright (c) 1995,98 Your Company }
{ }
{************************************************ ******}
The copyright notice must contain at least the following lines:
Copyright (C) Year Copyright Owner
If you are a third party developing software for Borland, you can add your own name at the end of the copyright:
{************************************************ ******}
{ }
{ Borland Delphi Visual Component Library }
{ Copyright (c) 1995,99 Borland International }
{ Created by Project JEDI }
{ }
{************************************************ ******}
2.2.2 unit declaration
Each unit file must have a unit declaration. unit is a reserved word, so it needs to be lowercase. The name of the unit can be mixed case, but must be the same as the file name of the unit file. For example:
unit MyUnit;
Then the name of the unit file should be MyUnit.pas. In the file system, it serves as the entry for this file.
2.2.3 uses statement
Within a unit, uses declarations should be bootstrapped with smaller uses. The referenced unit name follows the capitalization convention used when it is defined in its own unit:
usesMyUnit;
Each unit name is separated from its adjacent unit name by a comma, and the last unit name is followed by a semicolon:
uses
Windows, SysUtils, Classes, Graphics, Controls, Forms,
TypInfo;
It is also correct to add the unit name starting from the next line of uses and to add the unit name directly after uses.
uses Windows, SysUtils, Classes, Graphics, Controls, Forms,
TypInfo;
You can format your list of unit names to wrap within the 80-character limit, or to have one line per unit name.
2.2.4 Class and interface definitions
Class definitions begin with two spaces, followed by a prefix "T". The prefix should be capitalized, and each embedded word should begin with a capital case. Do not use the tab character "Tab" in Object Pascal source code. example:
TMyClass
Follow the identifier with a space, then an equal sign, then the word class, which should be lowercase:
TMyClass = class
If your class inherits from an ancestor, you need to add left and right brackets containing the ancestor class:
TMyClass = class(TObject)
Range indicators are two spaces from the margin and appear in the following order:
TMyClass = clss(TObject)
private
protect
public
published
end;
Data are usually declared only in the private section, and their identifiers start with "F". All such statements must be 4 spaces from the margin:
TMyClass = class (TObject)
private
FMyDate: Integer;
function GetDate: Integer;
procedure SetData(Value: Integer);
public
published
property MyData: Integer read GetData write SetData;
end ;
Interfaces follow the same rules as their counterparts, except that you should ignore scope indicators and private data, and use the word interface instead of the word class.
naming convention
Except for reserved words and indicators, which are lowercase, all Pascal identifiers should use camelCase format, that is, the first letter of each identifier should be capitalized, and the first letter of embedded words should also be capitalized. The same is true for abbreviations that only take the first letter. .
MyIdentifier
MyFTPClass
The main exception to this rule is the case of header file translations, which should follow the naming conventions in the original header files. For example:
WM_LBUTTONDOWN, do not write wm_LButtonDown.
Except for header file translations, do not use underscores to separate words. Class names should be nouns or noun phrases. The name of the interface or class depends on the obvious purpose and use of the interface.
Good name:
AddressForm, ArrayIndexOutOfBoundsException
Bad names:
ManageLayout //Use verb phrase
delphi_is_new_to_me //Use underline
3.1 Unit naming
See unit declaration
3.2 Class/Interface Naming
See class/interface declaration
3.3 Domain/field naming
Use camelCase format. Start with a capital "F" and declare all data in private, using properties or getters and setters to provide public access. For example: use the name GetSomething to name a function that returns an internal field/field value, and use SetSomething to name a procedure that sets a field/field value.
Do not use all uppercase letters in const sections unless required for header file translation.
Delphi was developed in California, so we discourage the use of tokens unless required for header file translation.
correct:
FMyString: string;
Incorrect:
lpstrMyString: string;
Of course the Hungarian nomenclature is preserved in the enumeration type definition:
TBitBtnKind = (bkCustom, bkOK, bkCancel, bkHelp,
bkYes, bkNo, bkClose, bkAbort, bkRetry,
bkignore, bkAll);
In this case the character bk is inserted before each element of this enumeration type. bk means ButtonKind.
When considering naming conventions, avoid using single-character names, except for zero-time variables and loop variables.
Avoid using the "l" (L) variable because it is difficult to distinguish from "1" (one) on either a printer or a monitor.
3.4 Method naming
Method naming also uses camelCase format. Method naming conventions are the same as naming non-const fields, but they can be distinguished from context. Method naming should enforce the use of verbs or verb phrases. For example:
//Good method naming:
ShowStatus, DrawCircle, AddLayoutComponent
//Poor method naming:
MouseButton //noun phrase, no description function
drawCircle //Start with lowercase letter
add_layout_component //Underline is used
//The functions of the following methods are not clear enough. Does it start running a service (better: StartServer) or determine whether a service is running (better: IsServerRunning)?
ServerRunning //Verb phrase, but not a command
A method to get or set some class properties should be called GetProperty or SetProperty respectively, where Property represents the name of the property. For example:
GetHeight, SetHeight
A method that tests a Boolean property of a class should be named IsVisible, where Visible represents the name of the property. For example:
IsResizable, IsVisible
3.5 Naming local variables
The naming rules for local variables are the same as those for fields/fields, except that "F" is not used. See section 3.3.
3.6 Reserved words
Reserved words and indicators should be in all lowercase. This can be a bit confusing at times. For example: Integer is an identifier and appears with the first letter capitalized. The string reserved words are all lowercase.
3.7 Type declaration
All type name declarations begin with the letter T and follow the same name as the class.
4.0 White space usage
4.1 Blank lines
Blank lines can improve readability by grouping logically related code segments. A blank line can also be used in the following places:
After the copyright comment block, the package declaration (package), and the import section (import).
between class declarations.
between method declarations.
4.2 Spaces
Object Pascal is a very clear and readable language. Usually, you don't need to add a lot of spaces to separate lines in your code. The following articles provide some guidelines for using whitespace:
4.2.2 Spaces should not be used:
Between the method name and the opening bracket;
Before or after the .(dot) operator;
between a unary operator and its operand;
Between a type and the expression it casts;
after the left bracket and before the right bracket;
after the left square bracket and before the right square bracket;
before a ban;
For example:
//Correct usage:
function TMyClass.MyFunc(var Value: Integer);
MyPointer := @MyRecord;
MyClass := TMyClass(MyPointer);
MyInteger := MyIntegerArray[5];
//Incorrect usage:
function TMyClass.MyFunc( var Value: Integer );
MyPointer := @MyRecord;
MyClass := TMyClass ( MyPointer ) ;
MyInteger := MyIntegerArray [ 5 ] ;
4.3 Indentation
You should always indent all indent levels by two spaces. In other words, the first level is indented by two spaces, the second level is indented by four spaces, the third level is indented by six spaces... Don't use the tab character Tab.
Of course, there are still a few exceptions. Reserved words like unit, uses, type, interface, implementation, initialization and finalization are always in top case. The last end identifier of the cell is also top-level. In the project file, program and the main begin and end are also in top grid. The main begin..end block needs to be indented by at least two spaces.
4.4 Continuation
Rows should be limited to 80 columns. Rows with more than 80 columns should be divided into multiple consecutive rows. All subsequent lines should follow the first line of this declaration and be indented by two characters of space.
For example:
//correct:
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 ;
if ((X = Y) or (Y = X) or
(Z = P) or (F = J) then
begin
S := J;
end ;
Do not wrap lines between a parameter and its type unless the list is comma separated, in which case wrap before the last parameter so that the type name begins on the next line. There should be no space between the colon and its variable, and there should be one space between the colon and the type name.
//correct:
procedure Foo(Param1: Integer; Param2: Integer);
//mistake:
procedure Foo( Param :Integer; Param2:Integer);
A subsequent line should not begin with a binary operator. Avoid cutting a line where whitespace does not normally occur, such as between a method name and its opening bracket, or between an array name and its opening bracket. If you must break lines in the above situation, break the line after the opening bracket or opening square bracket. Don't put begin on the same line as other code.
For example:
//mistake:
while (LongExpression1 or LongExpression2) do begin
//DoSomething
//DoSomethingElse;
end ;
//correct
while (LongExpression1 or longExpression2) do
begin
//DoSomething
//DoSomethingElse;
end ;
if (LongExpressiong1) or
(LongExpression2) or
(LongExpression3) then
5.0 Comments
The Object Pascal language supports two types of comments: block and single-line comments. Here are some annotation usage guidelines:
·It is helpful to place comments at the top of the unit explaining the purpose of the unit
·It is helpful to place comments before class declarations
·It is helpful to set annotations before method declarations
·Avoid comments with obvious meanings
i := i + 1; //Add one to i
·Remember that easily misinterpreted comments are more harmful than no comments at all.
·Avoid putting information in comments that seems to be invalid
·Avoid embedding asterisks or other typographical symbols in the borders of comments
·Zero-hour comments, that is, comments that need to be changed or deleted, should be marked with "???:" before them, so that they are easy to find. Conceptually, all zero-time comments should be deleted before the program is released.
// ???: Change this to call Sort when it is fixed
List.MySort;
5.1 Block comments
Object Pascal supports two types of block comments. The most commonly used are comments enclosed in a pair of curly braces {}. The Delphi team kept the comments as few and simple as possible. For example: You should avoid using asterisks to create patterns or lines in your comments. Instead, use spaces to separate your comments, just as you would in a word processing document. The words in your comment should start on the same line as the first curly brace, as in the following excerpt from DsgnIntf.pas:
{ TPropertyEditor
Edits a property of a component, or list of components,
selected into the Object Inspector. The property
editor is created based on the type of the
property being edited as determined by the types
registered by...
etc...
GetXxxValue
Gets the value of the first property in the
Properties property. Calls the appropriate
TProperty GetXxxValue method to retrieve the
value.
SetXxxValue Sets the value of all the properties
in the Properties property. Calls the appropriate
TProperty SetXxxxValue methods to set the value. }
Block comments are often used in copyright comments. Also used to comment out some lines of code.
A block comment explaining the purpose of the method should be preceded by the method declaration.
For example:
// CORRECT
{ TMyObject.MyMethod
This routine allows you to execute code. }
procedure TMyObject.MyMethod;
begin
end;
// INCORRECT
procedure TMyObject.MyMethod;
{************************************************ *****
TMyObject.MyMethod
This routine allows you to execute code.
*************************************************** *****}
begin
end;
The second type of block comment contains two characters, parentheses and an asterisk: (* *). This is sometimes called a star bracket comment. These annotations are generally only useful during code development, and their main benefit is that they allow nested annotations to be less than two levels deep. Object Pascal does not support the same type of nesting of comments, so there is really only one level of nesting: curly braces inside star braces, or star braces inside curly braces. Standard Pascal annotations of other types within annotations of this type will be ignored as long as you do not nest them. Therefore, you can use this syntax to comment out a large block of code that contains both code and comments:
(* procedure TForm1.Button1Click(Sender: TObject);
begin
DoThis; // Start the process
DoThat; // Continue iteration
{ We need a way to report errors here, perhaps using
a try finally block ??? }
CallMoreCode; // Finalize the process
end ; *)
In this case, the entire Button1Click method is commented out, including any sub-comments within it.
5.2 Single-line comments
A single-line comment consists of a comment character // and its leading text, with a space between the text and the comment character. If single-line comments are on different lines than the code, they must have the same indentation level as the code. You can use multiple single-line comments to form a large comment.
A blank line is required before a single-line comment or group of comments, unless it is the first line of a block. If comments are used for several statements, comments and comment groups should be followed by a blank line. If the comment only explains the statement on the line that follows it, it does not need to be followed by a blank line.
For example:
// Open the database
Table1.Open;
Single-line comments can also follow the code declarations they explain. Such comments are sometimes referred to as tracking comments. There must be at least one space between them and the code. If multiple trace comments appear simultaneously in a block of code, these comments need to be aligned.
For example:
if ( not IsVisible) then
Exit; // nothing to do
Inc(StrLength); // reserve space for null terminator
Avoid tracing comments on each line of executable code. It is usually best to limit the use of comments, or even leave them empty, between the begin..end blocks of a method or function. Long comments can appear in block comments before the definition of methods and functions.
kind
6.1 Organization of classes
The organization of class bodies should follow the following order:
·Domain/field declaration
·Method declaration
·Attribute definition
Domains/fields, properties, and methods should be alphabetical-indexed by their names.
6.1.1 Access levels
Except for code generated by the IDE, the scope designators for classes should be in the following order:
·private statement
·protect statement
·public statement
·Published statement
In Object Pascal, class members have four access levels: published, public, protected, and private—in order of decreasing access capabilities. The default access level is published. Generally, a member should be assigned the lowest access level that is most appropriate for it. For example: members that can only be accessed by other classes in the same unit should be declared private. At the same time, declaring members with low access levels also gives the compiler the opportunity to improve optimization. Of course, on the other hand, using low access levels makes it difficult to extend subclasses. If there is reason to believe that a class will be subclassed at some point in the future, then those members that need to be inherited and extended by the subclass should be declared as protected. Properties used to access private data can also provide this protection.
You should prohibit public access to the data. Data is usually declared in the private section, and any public access to it should be done through GetXXX, SetXXX methods or properties.
6.1.8 Constructor declaration
Methods need to be sorted by alphabetical index. It is correct to place the constructors and destructors at the beginning of the public section or arrange them alphabetically.
If there are multiple constructors or you use multiple constructors with the same name, they should be arranged according to the parameter list, with the one with the fewest parameters in front of the one with the most parameters. This means that if there is a constructor without parameters, it must appear first. In order to maintain the best compatibility with C++Builder, the parameter list of the constructor should be unique. C++ does not call a constructor based on its name, so the only way to distinguish multiple constructors is through its argument list.
6.2 Method declaration
If possible, method declarations should appear on a single line.
For example:
Examples:
procedure ImageUpdate(Image img, infoflags: Integer,
x: Integer, y: Integer, w: Integer, h: Integer)
7.0 Interface
An interface declaration has the same form as a class declaration:
InterfaceName = interface ([ inherited Interface ])
InterfaceBody
end ;
Interface declarations should be indented by two spaces, interface bodies by four spaces, and end characters by two spaces.
There are no fields/fields in the interface declaration. But attributes can appear.
All interface methods are inherently public and abstract, and there is no need to include such keywords in the interface declaration.
Unless otherwise noted, interface declarations have the same style as declarations of the same type.
7.1 Interface body organization
The interface body can be organized in the following order:
·Interface method declaration
·Interface attribute declaration
Interface methods and properties are declared in the same style as classes.
8.0 Statement
A statement is a line or lines of code ending with a seal. A single statement has only one ban number, and a compound statement has multiple ban names.
//This is a single statement:
A := B;
//This is a compound statement:
begin
B := C;
A := B;
end ;
8.0.1 Single declaration
If a single statement needs to be wrapped, it needs to be indented two spaces relative to the previous line.
//For example:
MyValue :=
MyValue + (SomeVeryLongStatement / OtherLongStatement);
8.1.1 Assignment and expression declarations
There can be at most one statement per line.
For example:
a := b + c; Inc(Count); //Error
a := b + c; //Correct
Inc(Count); //Correct
8.1.2 Local variable declaration
Local variables also use camelCase format. Do not use the leader "F", which is reserved for fields/fields in class declarations.
For example:
var
MyData: Integer;
MyString: string ;
You can declare multiple variables of the same type on the same line:
var
ArraySize, ArrayCount: Integer;
This declaration habit is not recommended in class declarations.
8.1.3 Array declaration
It's common to always put a space before the opening bracket and after the closing bracket:
type
TMyArray = array [0..100] of Char;
8.2.3 if statement
The if statement must appear in at least two lines:
For example:
//mistake:
if A = B then DoSomething;
//correct
if A = B then
DoSomething;
If it is a compound if statement, there should be a new line for each delimiter:
//mistake:
if A = B then begin
DoSomething;
DoSomethingElse;
end else begin
DoThis;
DoThat;
end ;
//correct
if A = B then
begin
DoSomething;
DoSomethingElse;
end
else
begin
DoThis;
DoThat;
end ;
A few variations of the following can be adopted:
//correct
if Condition then
begin
DoThis;
end else
begin
DoThat;
end ;
//correct
if Condition then
begin
DoThis;
end
else
DoSomething;
//correct
if Condition then
begin
DoThis;
end else
DoSoemthing;
//The following method may not be cared about, but it is worthy of praise:
if Condition then
DoThis
else DoThat;
8.2.4 for statement
Example:
// INCORRECT
for i := 0 to 10 do begin
DoSomething;
DoSomethingElse;
end ;
//CORRECT
for i := 0 to 10 do
begin
DoSomething;
DoSomethingElse;
end ;
8.2.5 while statement
Example:
// INCORRECT
while x < j do begin
DoSomething;
DoSomethingElse;
end ;
// CORRECT
while x < j do
begin
DoSomething;
DoSomethingElse;
end ;
8.2.6 repeat until statement
Example:
//CORRECT
repeat
x := j;
j := UpdateValue;
until j = 25;
8.2.7 case statement
Example:
//CORRECT
case Control.Align of
alLeft, alNone: NewRange := Max(NewRange, Position);
alRight: Inc(AlignMargin, Control.Width);
end ;
// CORRECT
case x of
CSStart:
begin
j := UpdateValue;
end ;
csBegin: x := j;
csTimeOut:
begin
j := x;
x := UpdateValue;
end ;
end ;
// CORRECT
case ScrollCode of
SB_LINEUP, SB_LINEDOWN:
begin
Incr := FIncrement div FLineDiv;
FinalIncr := FIncrement mod FLineDiv;
Count := FLineDiv;
end ;
SB_PAGEUP, SB_PAGEDOWN:
begin
Incr := FPageIncrement;
FinalIncr := Incr mod FPageDiv;
Incr := Incr div FPageDiv;
Count := FPageDiv;
end ;
else
Count := 0;
Incr := 0;
FinalIncr := 0;
end ;
8.2.8 try statement
//Correct
try
try
EnumThreadWindows(CurrentThreadID, @Disable, 0);
Result := TaskWindowList;
except
EnableTaskWindows(TaskWindowList);
raise ;
end ;
finally
TaskWindowList := SaveWindowList;
TaskActiveWindow := SaveActiveWindow;
end ;
Author's Blog: http://blog.csdn.net/sailerbai/