Delphi Coding Standards
July 30, 2010 Friday

TABLE OF CONTENTS

| Introduction | General Principles | Naming Conventions |
| Layout Standards | General Layout Guidelines | General Practices |



INTRODUCTION

This is the first issue of the document describing Delphi coding standards. These standards are intended to be used as additions to the general coding standards described in PROC/PG/STD/001, but in cases where the two standards conflict this standard takes precedence.  


GENERAL PRINCIPLES

A significant portion of the code of most Delphi programs is generated automatically by Delphi. The standards in this document are intended to be compatible with those used for the automatically-generated code. In particular there should never be a need to manually alter or re-format the automatically-generated code.

 


NAMING CONVENTIONS


Throughout this section the term routine is used to mean procedure and/or function.

The term descriptive name means a name made up of one or more words or abbreviations written in mixed case, with upper case being used for the first letter of each word or abbreviation.

FILES, FORMS & UNITS

The name used for the project file (xxx.DPR) is also the program name (i.e. the EXE file will be named xxx.EXE). A suitable descriptive name is used.

Form and unit filenames (xxx.DFM/xxx.PAS) are also used as the unit names (i.e. the unit will be named xxx). Suitable descriptive names are used.

Form names (i.e. the Name property of objects derived from TForm) are descriptive names. Form names should be prefixed with a "frm". Data module names should be prefixed with a "mod". The descriptive name is related to the form's filename (typically the filename is an abbreviation of the descriptive name). E.g. a form named frmOrderEntry might be stored in the file ORDENT.DFM. Note that Delphi automatically names the form's class as T<form name and automatically declares a global instance of the class named <form name. E.g. in the previous example the form's class would be TfrmOrderEntry and there would be the following declaration in the interface section

var   frmOrderEntry: TfrmOrderEntry;

COMPONENTS ADDED VIA THE OBJECT INSPECTOR

Components which are not referenced by any code and which do not have any associated events (e.g. panels, labels) are left with the default name assigned by Delphi. The names of all other components are set to a descriptive name prefixed by a short identifier (3-4 character lower case) as shown in the following list. The list can be expanded as necessary if other components are used.  
Identifier  Components
act TActionList 
anm Tanimate 
bmv TBatchMove 
btn TButton, TBitBtn, TSpeedButton (and other button derivatives) 
bvl TBevel 
cal TDateTimePicker, TMonthCalendar 
cds TClientDataSet 
chk TCheckBox, TDBCheckBox 
cht TChart, TDBChart 
cmb TComboBox, TDBComboBox, TDBLookupCombo, TDriveComboBox, TFilterComboBox (and other combo derivatives) 
con TDCOMConnection, TCorbaConnection, TSocketConnection, TOLEnterpriseConnection, TMidasConnection 
db TDatabase 
dde TDDEClientConv, TDDEClientItem, TDDEServerConv, TDDEServerItem 
dlg Common dialogs (all components on the Dialogs tab) 
edt TEdit, MaskEdit, TDBEdit (and other edit control derivatives) 
grd TStringGrid, TDrawGird, TDBGrid, TDBCtrlGrid (and other grid derivatives) 
grp TGroupBox 
hdr THeader (Win 3.1), THeaderControl (Win 32) 
hot THotKey 
ilst TImageList 
img TImage, TDBImage 
lbl TLabel, TStaticText, TDBText (and other label derivatives) 
lst TListBox, TCheckListBox, TDBListBox, TDBLookupList, TFileListBox, TdirectoryListBox (and other list box derivatives) 
lvw TListView 
med TMediaPlayer 
mem TMemo, TDBMemo, TRichEdit 
mnu TMainMenu and menu items (see below for more on menu items) 
nav TDBNavigator 
ntbl TNestedTable 
nbk TNotebook (Win 3.1) 
ole TOLEContainer 
obj TSimpleObjectBroker (and descendants) 
out TOutline (and other TOutline derivatives) 
pbar TProgressBar 
pag TPageControl 
pscr TPageScroller 
pbox TPaintBox 
pnl TPanel (and other panel derivatives) 
pop TPopupMenu (see below for more on menu items) 
prov TProvider, TDataSetProvider 
qry TQuery (fields are always left with the default names assigned by Delphi) 
rdo TRadioButton 
rgrp TRadioGroup, TDBRadioGroup 
rpt TQuickRep 
rsvr TRemoteServer 
sbar TStatusBar 
sbox TScrollBox 
scr TScrollBar 
shp TShape 
spl TSplitterBar 
spn TUpDown (spinner) 
src TDataSource 
ssn TSession 
sp TStoredProcedure 
tab TTabset (Win 3.1), TTabControl (Win 32) 
tbar TToolBar, TCoolBar 
tbk TTabbedNotebook (Win 3.1) 
tbl TTable (fields are always left with the default names assigned by Delphi) 
tim TTimer 
trk TTrackBar 
tvw TTreeView 
upd TUpdateSQL 
Menu items can usually be left with the default names assigned by Delphi. However, where there is a lot of manipulation of menu items (e.g. checking/unchecking or enabling/disabling) it may be clearer to explicitly name them. The naming scheme used is analogous to that used by Delphi to automatically name fields, and is best explained by example:

  • The File item for a menu named mnuMain is named mnuMainFile.
  • The Save As item within this file menu is named mnuMainFileSaveAs.
  • The Arrange Icons item for a popup menu named popIconBox is named popIconBoxArrangeIcons.

CLASSES, RECORDS & TYPES

Classes, records and types are named using the Delphi convention of T<descriptive name. Some exceptions are:

  • Exception classes are named E<descriptive name.
  • Pointer types are named P<name of type pointed to without the leading "T".

CLASS & RECORD MEMBERS

Class member routines and properties are given descriptive names.

Routines which are specified in the read or write clauses of property declarations are named Get<property name and Set<property name respectively.

Event handlers created via the object inspector are normally left with the default name allocated by Delphi (which is descriptive, containing both the component name and the event). The exception is if one event handler is to handle several events (from the same or different components), in which case it should be renamed replacing the appropriate part of the name with an appropriate description (e.g. a handler which handles the OnClick event for a group of buttons concerned with paging might be renamed from btnPageBackClick to btnPagingClick).

Event handlers not created via the object inspector should be given names which follow the naming convention used by Delphi. In particular event handlers for application events should be named OnApp<event description, e.g. OnAppActivate.

Procedures which are Windows message handlers are given the name On<message name, e.g. OnWmGetMinMaxInfo.

Constructors and destructors are normally named Create and Destroy respectively.

Class and record member variables are named using the Delphi convention of F<descriptive name. For class member variables corresponding to properties <descriptive name is the same as the property name.

GLOBALS

Global data items (other than the global form declarations automatically generated by Delphi) and global routines are named g<descriptive name.

MEMBERS OF ENUMERATED TYPES

Members of enumerated types are given descriptive names prefixed by a short (typically 2-letter) lower case string which indicates the type.

CONSTANTS

This section applies to true constants (other than members of enumerated types) and to typed constants which are used like true constants but which have to be typed because of language constraints (e.g. arrays).

Constant names are descriptive names written entirely in upper case. Underscores should be used if necessary to separate words.

Related constants should be grouped together. Each constant in the group should begin with the same short prefix.

OTHER

Descriptive names are used for all cases not covered explicitly above.


LAYOUT STANDARDS

INDENTATION

A standard indentation level of 2 characters is used throughout a source file.

COMMENTS

The {...} style of comment is normally used. The (*...*) style is used to temporarily comment out blocks of code. Starting with Delphi 2.0 the // end of line comment, denoting that the rest of the text on the line is a comment, is available. Since this type of comment is not backward compatible, use of the end of line comment must be restricted to code that has no chance whatsoever of being used in any Delphi version lower than 2.0.

The requirements in PROC/PG/STD/001 for a routine header comment on every routine is relaxed for event handlers with self-explanatory names (e.g. btnCloseClick) and no more than a few lines of code. Where there are a number of related event handlers they may be grouped together with a header comment for the group.

VARIABLES

Variables and constants should be placed with one declaration per statement. Object Pascal allows multiple declarations per statement :

First, Last, Current: Integer;

But the following layout with one declaration per line is the preferred layout :

First  : Integer; Last   : Integer; Current: Integer;


GENERAL LAYOUT GUIDELINES

The general layout of classes, record and type declarations, variables etc. follows that for the code automatically generated by Delphi and for the code in the Delphi VCL. For example the following code shows how the start of a unit (stripped of comments) might be layed out:

{$I AADefs.Inc}
					
Unit Main;
					
interface
					
uses
    SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
    Controls, Forms, Dialogs, ExtCtrls;

const MAX_ARRAY_SIZE = 32000; MAX_ENTRY      = 128; MIN_ENTRY      = 1; type TBigIntArray = array[0.. MAX_ARRAY_SIZE] of integer; PBigIntArray = ^TBigIntArray; TLine = record FStart: TPoint; FEnd: TPoint; end; type TMainForm = class(TForm) btnClose: TButton; procedure btnCloseClick(Sender: TObject); private { Private declarations } procedure ApplicationIdle(Sender: TObject; var Done: Boolean); public { Public declarations } end; var MainForm: TMainForm;

Delphi always adds new event handlers to the end of the source file. To facilitate good layout, add custom form methods at the top of the source, above the first event handler , preferably in alphabetical order. Mark the start of the event handler and method sections with a comment, i.e. "{Event Handlers}" or "{Methods}".

As illustrated below, two types of "begin end" formatting are acceptable. However; the latter is greatly prefered and will be encouraged as the standard. The second form is the only format used in the examples in this document.  

<control structure>
    begin 
    .... 
    end

and

<control structure> begin ....	end

INITIALIZATION CODE

Initialization code is written at the end of a unit between the initialization keyword (although begin is accepted instead of initialization it should not be used) and the final "end.". The code is indented by one indentation level.

FINALIZATION CODE

Delphi 2.0 adds the finalization keyword. It is meant to be a counterpart to the initialization section of a unit. Use of finalization must be restricted to code that has no chance whatsoever of being used in any Delphi version lower than 2.0.

PROCEDURES & FUNCTIONS

Within a unit file, routines are normally arranged alphabetically. Within a form file, routines are best grouped into event handler and non-event handler sections. Non-event handler routines (e.g. anything not inserted automatically by Delphi) should be arranged alphabetically starting at the top of the implementation section. The event handler routines may be arranged grouped by function (e.g. button click handlers), alphabetically, or left as placed by Delphi.

Within a function the result is assigned using the Result keyword rather than the function name.

In general use one parameter per declaration when declaring a procedure or function. Pascal allows mutiple parameters per declaration statement :

procedure FindItem(First,Last,Current: Integer; Value: String);

But the better layout is :

procedure FindItem(First: Integer; Last: Integer; Current: Integer; Value: String);

If a procedure or function declaration spans multiple lines, the code should be formatted so that it does not split a parameter declaration over two lines.

IF THEN/ELSE CONDITIONAL STATEMENTS

The following examples show the preferred layouts:

if PositionChanged then 
begin
    SavedX := X;
    SavedY := Y;
end;
if (Balance < 0) then 
begin
    Interest := Balance * OverdraftRate;
    Dec(Balance, Interest);
end	
else 
begin
    Interest := Balance * DepositRate;
    Inc(Balance, Interest);
end;

The non-existent elseif construct can be simulated as follows:

if Condition1 then begin { statements } end else if Condition2 then begin { statements } end else begin { statements } end;

For very simple cases the following examples are acceptable:

if Highlighting then Color := clRed;
if (Row = 0) or (Col = 0) then
    Color := clHeading
else
    Color := clBody;

Where the condition includes logical or bit operators brackets are always used to explicitly show the order of evaluation. Optionally the entire condition may be enclosed in brackets (the preferred method).

FOR, WHILE AND REPEAT LOOPS

The following examples show the preferred layouts:

for ItemIndex := 0 to ItemCount-1 do 
begin
    { statements }
end;

while (not tblOrders.EOF) do 
begin
    { statements }
end;

repeat
    { statements }
until (x = 99);

For very simple cases the following examples are acceptable:

for i := Low(Squares) to High(Squares) do
    Squares[i] := i*i;
while ((i > 0) and (Str[i} = SPACE)) do
    Dec(i);

Where the condition for while and repeat loops includes logical or bit operators brackets are always used to explicitly show the order of evaluation. Optionally the entire condition may be enclosed in brackets (the preferred method).

If the body of a for or while loop is null then the following layout is used.

while (ProcessNextRecord = OK) 
         ;

CASE STATEMENTS

The following example shows the preferred layout:

case InputChar of   `A`..`Z`, `a`..`z`: begin     { statements }   end;   `0`..`9`: begin     { statements }   end;   else begin     { statements }   end; end;

If only a single statement is required for each condition the following is acceptable:

case State of   stInitializing: Prepare;   stAborting:     ShutDown;   else            NormalProcessing; end;

All case statements should contain either an else clause or a comment explaining why the else clause is not required.

WITH

The following example shows the preferred layouts:

with Sender as TStringGrid do begin   { statements } end; for i := 1 to LastPiece do begin   with Sender as TChessBoard do     { statements processing Piece[i] } end; for i := 1 to LastPiece do with Sender as TChessBoard do begin   { statements processing Piece[i] } end;

CONCATENATED CONTROL STRUCTURES

In situations like the following:

<control structure 1> begin   <control structure 2> begin     { statements }   end; end;

where there is no doubt that there will never be a requirement for statements between the two ends (i.e. in which the two control structures logically belong together) the control structures may be concatenated,  e.g.:

for Col := 1 to LastCol do    for Row := 1 to LastRow do begin     { statements processing Cell[Col, Row] }   end;

TRY...FINALLY/TRY...EXCEPT

The following examples show the preferred layouts:

try   { statements } finally   { statements } end; try   { statements } except   { statements } end; try   { statements } except   on EDivByZero do begin     { statements }   end;   on E: EDatabaseError do begin     { statements }   end;   else begin     { statements }   end; end;

When both finally and except clauses are required then two levels of nesting must be used, as in:

try   try     { statements }   finally     { statements }   end; except   { statements } end; try   try     { statements }   except     { statements }   end; finally   { statements } end;

Note that the two examples above are equivalent except for the order in which the exception statements and finally statements are executed if an exception is raised.

SEMICOLONS

Enter all allowable semicolons in the source code. While the following code will compile :

procedure Test; begin   if (x <> 1) then begin     for y := 1 to 10 do begin     end   end end end.

It should be formatted like :

procedure Test; begin   if (x <> 1) then begin     for y := 1 to 10 do begin     end;   end; end; end.

OTHER

The EXIT, CONTINUE and BREAK keywords should be capitalized to draw attention to their use.  


GENERAL PRACTICES

COMPILER DIRECTIVES & CONDITIONAL COMPILATION

Use of the conditional compilation capabilities is acceptable. When defining conditional symbols, symbol names should be prefaced with one underscores and the symbol should be in all uppercase characters (e.g. _DEBUG). The underscore helps to keep conditionals separate form constants.

To make sure that all developers use the same compiler options a standard set of compiler directives will be included in each source file. This will also ensure that debugger information is removed from the release versions of applications.

These directives will be kept in the file AADefs.Inc. They should be inserted in source code by adding the include file statement {$I AADEFS.INC} as the first line in each source file. The conditional "_DEV_VER" should be defined for development versions of an application. The conditional "_RELEASE_VER" should be defined for the final release version of an application. "_RELEASE_VER". These conditionals can most easily be defined using project options in the Delphi IDE.  

TABLE OF STANDARD COMPILER SETTINGS

Compiler Directive  Notes
M Delphi 1.0 only. Set stack and heap size. 
MINSTACKSIZE 16384 Delphi 2.0 and higher only. Set minimum stack size. 
A+ Align data on word boundaries. 
B- Short circuit boolean expression evaluation. 
D Generate debugger information on for Debug,off for Release. 
F- Force far procedure calls off. 
G+ Generate 80286 code (Windows protected mode). 
I+ IO errors generate exceptions. 
K+ Use smart callbacks. 
L+ Generate local symbol information for debugging, on for Debug off for Release. 
N+ Numeric coprocessor support and floating point types : Single, Double, Extended, and Comp. 
P+ Open String parameters, allow string variables of varying sizes to be passed to the same procedure or function. 
Q+ Controls the generation of arithmetic overflow checking code, on for Debug off for Release. 
R+ Generation of range-checking code, on for Debug off for Release. 
S+ Generate stack-overflow checking code. 
T- No type checking of pointer value the @ operator returns when applied to a variable reference. 
V- No type-checking on strings passed as variable parameters. 
W+ Generates special prolog and epilog code for far procedures and functions (stack frames), on for Debug off for Release. 
X+ Turn extended syntax on. 
Y+ Controls generation of symbol reference information for debugging purposes, on for Debug off for Release. 
J+ Delphi 2.0 and higher only. Controls whether typed constants can be modified, on for Delphi 1.0 compatibility. 
H+ Delphi 2.0 and higher only. Turn long strings on. 
HINTS ON Turns compiler hints on, Delphi 2.0 and higher only. 
WARNINGS ON Turns compiler warnings on, Delphi 2.0 and higher only. 
OPTIMIZATION ON Turns compiler optimizations on, Delphi 2.0 and higher. Off for Debug On for Release. 
ASSERTIONS ON Turn assertions on, Delphi 3.0 and higher. On for Debug off for Release. 
 

FORMS

In general, global form variables are not allowed.

Forms may only access forms they have created or that are explicitly passed through parameters or properties.

Child forms should never refer to a parent form.

Procedures and functions that are not form methods should not be placed in form source files.

PROCEDURES & FUNCTIONS

The use of the Exit command to leave a procedure or function will be limited.

Make use of constant formal parameters to routines whenever possible. String and record parameters that are not declared as variable should normally be declared as constant.

IF THEN/ELSE CONDITIONAL STATEMENTS

Whenever possible, conditional statements should test for the positive rather than the negative result.

The use of the Exit command in the statement block of a conditional is strongly discouraged. Rather than using an Exit command, add an else clause or rewrite the conditional test.

FOR, WHILE AND REPEAT LOOPS

The use of Exit, Break and Continue commands to exit loops will be limited.

INCLUDE FILES

Include files files (using $I or $INCLUDE compiler directive) should be used only to define global compiler settings. Never use include files to define constants or variables. To define global variables or types, create a standard pascal unit with an interface section and no implementation section.

OTHER

Do not use the "old-style" objects (declared with the object keyword).

Do not use the "Real" floating point number type, use "Single" or "Double" instead.

If any sections of code are particularly dependent on properties set in the Object Inspector then ensure that the dependency is commented.

Make class destructors virtual.

Use the variant type (Delphi 2.0, 3.0) only where necessary.

Create common subroutines. If a particular section of code is used two or more times it should probably be placed in a separate procedure, function, or method.

Inline strings should not be used. Strings should be placed into string constants or string resources.

DELPHI CODING TIPS

Remember that in Object Pascal, the programmer must destroy what he/she creates. There is no garbage collection for memory, or automatic destruction of objects. If you allocate it, make sure you free it.

Nothing is automatically initialized (or uninitialized) in Object Pascal. Do not count on the value of any variable being zero, nil, or anything else unless you have set it. When an object is freed the pointer to that object is not cleared. Do not expect a pointer to be nil unless you have explicitly set to that value.

When writing classes, if you add a constructor then you probably need to add a destructor. It is best to write constructor and destructor methods at the same time, to make sure you destroy what you create. The same principle applies to forms, if you implement an OnCreate event handler, you proabably need an OnDestroy handler.

Don't forget to use override in place of virtual, when you override virtual methods.

Generally avoid the use of TTable components with any SQL database or when the database connection is through an ODBC driver.

Gokmen Portal - (Delphi Coding Standards)

Delphi Coding Standards
July 30, 2010 Friday