|
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.
|