Delphi coding standards - DelphiWorlds/CodingStandards GitHub Wiki

Delphi Coding Standards

Prologue

Intention

This is a set of coding standards for Delphi only. Not every coding situation is catered for in this document. Where you are unsure, please consult a senior developer and/or the Development Team Leader. This may lead to the document being further enhanced.

Examples

There are already many examples of code following these coding standards in the existing Kastri Free library, so please use those as a reference.

Breaches of the standards

If you notice any code that does not adhere to the standards, please notify the Development Team Leader.

Code organisation

This document outlines standards for what is contained in the source files themselves. For overall organisation of code within projects, please refer to the Delphi Worlds Application Construction document. (To be doc'd)

Naming

Note: Where naming rules are used, the case of the naming must match as per examples given

Namespacing

Unit files are divided into logical namespaces. Framework code (shared amongst projects) are to use the DW prefix.

Framework units should be named using the Embarcadero naming style.

For platform api units: <prefix>.<platform>api.<functionality>.pas e.g. DW.iOSapi.Photos.pas

For platform agnostic units: <prefix>.<functionality>.pas e.g. DW.SuperWidget.pas

For platform specific units that consume a platform api: <prefix>.<functionality>.<platform>.pas e.g. DW.SuperWidget.iOS.pas

Project specific units are to use the codename for that project: <projectprefix>.<unitname>.pas e.g MM.Types.pas

Unit naming

Form and frame units shall use the format <prefix>.View.<viewname> e.g SDX.View.Main.pas

Component Naming

Components shall have a meaningful name, and be suffixed with the component type, minus the T and vendor prefix, e.g: a TLayout might be named UpperLayout, a TEdit might be named UsernameEdit, a TFDTable might be named ListsTable.

Type naming

All distinct types shall be named starting with a capital T except for references imported from a macOS/iOS or Java API

Event handler method naming

Event handler methods that are assigned at runtime shall be named logically, pertaining to the event they are handling (with the word “On” removed, if it has one), and be suffixed with the word: Handler e.g. if a handler is assigned in code to the OnClick event of a button called CancelButton, the handler should be named: CancelButtonClickHandler

Class constructor and destructor method naming

Class constructors shall be named CreateClass, class destructors shall be named DestroyClass

Variable naming

Field variables (i.e. in classes) are to be prefixed with a capital F

Local variables are to be prefixed with a capital L, with the exception of integer loop variables, which should be named I for the first, J for the second, and so on.

Boolean properties shall be named with a prefix of a verb indicating the status, e.g. IsDefault, HasChildren, CanFocus

Declarations

Types

Type declarations may optionally be separated by one blank line, except in the case of classes and records, which shall always be separated by one blank line.

Classes

Visibility specifiers shall be on a new line, in order of the visibility, i.e. the order is:

strict private

private

strict protected

protected

public

published

For each visibility, field variables shall appear first, in alphabetical order, with the exception of event reference variables (e.g FOnClick), which shall follow the other field variables. The event reference variables shall also be in alphabetical order.

Methods shall follow the field variables, also in alphabetical order, however class constructors/destructors and instance constructors/destructors shall appear first. Additionally, class variables and methods shall appear in their own visibility section, which will appear before the visibility section that has instance declarations, e.g:

type
  TReachability = class(TObject)
  private
    class var FCurrent: TReachability;
    class constructor CreateClass;
    class destructor DestroyClass;
    class procedure ReachabilityCallback(reachability: SCNetworkReachabilityRef; flags: SCNetworkReachabilityFlags; info: Pointer); cdecl; static;
  private
    FConnectivity: TConnectivity;
    FIsConnectedToInternet: Boolean;
    FIsWifiInternetConnection: Boolean;
    FReachabilityRef: SCNetworkReachabilityRef;
    procedure ReachabilityChanged(const AFlags: SCNetworkReachabilityFlags);
    function Start: Boolean;
    procedure Stop;
    procedure UpdateReachability(const AFlags: SCNetworkReachabilityFlags);
  public
    class property Current: TReachability read FCurrent;
  public
    constructor Create;
    destructor Destroy; override;
    property Connectivity: TConnectivity read FConnectivity write FConnectivity;
    property IsConnectedToInternet: Boolean read FIsConnectedToInternet;
    property IsWifiInternetConnection: Boolean read FIsWifiInternetConnection;
  end;

Methods that are implementation of interfaces: these shall always appear in their own protected section, preceded by a braced comment that indicates the interface being implemented, e.g. (code snipped for brevity):

type 
  TMobileTitlesView = class(TFrame, ITitlesView, IFormKeyEventsListener) 
  protected 
    { ITitlesView } 
    procedure Activate(const AActivate: Boolean); 
    procedure ApplyFilter(const AFilter: string); 
    { IFormKeyEventsListener } 
    function HandleKeyDown(var Key: Word; var KeyChar: Char; Shift: TShiftState): Boolean; 
  end;

Properties shall follow the methods, also in alphabetical order, with the exception of event properties, which shall follow the other properties, however the event properties shall also be in alphabetical order

The only exception to the above rules is for IDE generated classes: forms, frames and datamodules, where the auto-generated published component and event handler references appear first.

Interfaces

Interfaces shall be declared similarly to classes, with method declarations first, in alphabetical order, then properties, also in alphabetical order

Code Structures

with

Under no circumstances is with to be used. No discussions will be entered into.

Uses clauses

Units that are used by another, should appear in the section of lowest scope, i.e. if a unit imports symbols from another unit that are required only in the implementation section, the used unit shall appear in the implementation section of the unit using it. This is to avoid creating greater dependency than is required, and avoids having to resolve circular references.

finally Blocks

Code inside a finally block is to execute resource deallocation, e.g. calling Free, DisposeOf, releasing of handles, etc, or restore a related state, e.g. calling EndUpdate, or resetting a flag etc.

Checking For Assignment

Use the Assigned method for event handlers only. All other checks for assignment shall be compared against nil, e.g:

  if Assigned(FOnClick) then
    FOnClick(Self);

  if FConnectivity <> nil then
    TOpenConnectivity(FConnectivity).DoConnectivityChange(FIsConnectedToInternet);

Global Variables

Global variables are to be avoided, unless there is a very good reason to use them. They must be approved by the Development Team Leader. Globals created by the IDE such as frmMain etc are to be ignored. Instead, consider the scope of use, and perhaps access via a class variable.

Formatting

Margin

The margin is set at 150 characters. Type declarations, Whole expressions and individual parameter declarations including their type, going beyond the margin, shall be moved to the next line, and be indented by 2 spaces. Unit names in unit clauses going beyond the margin shall also be moved to the next line, at the same indentation as the line before, e.g a declaration on one line may look like this: (code snipped for brevity)

  PubNub = interface(NSObject)
    ['{6B36F91F-8B1E-4510-B71B-29634CF4B898}']
    [MethodName('historyForChannel:start:end:limit:includeTimeToken:withCompletion:')]
    procedure historyForChannelShouldIncludeTimeToken(channel: NSString; startDate: NSNumber; endDate: NSNumber; limit: NSUInteger; shouldIncludeTimeToken: Boolean; block: PNHistoryCompletionBlock); overload; cdecl;
  end;

Column 150 is the first T in shouldIncludeTimeToken, so the line should break after limit: NSUInteger;, thus will end up looking like this:

  PubNub = interface(NSObject)
    ['{6B36F91F-8B1E-4510-B71B-29634CF4B898}']
    [MethodName('historyForChannel:start:end:limit:includeTimeToken:withCompletion:')]
    procedure historyForChannelShouldIncludeTimeToken(channel: NSString; startDate: NSNumber; endDate: NSNumber; limit: NSUInteger;
      shouldIncludeTimeToken: Boolean; block: PNHistoryCompletionBlock); overload; cdecl;
  end;

(See also Spaces within lines section)

Case

Keywords (represented by bold in the Delphi IDE) are to be all lowercase. The only exception is for Message, when it is not being used to indicate a message method.

Constants are to start with a lowercase c.

All other identifiers are to start with a capital first letter

Indentation

• All declaration section headers (uses, type, const, procedure etc) are to start in the first column

• All declarations within a declaration section are to be indented by 2 spaces

• Code blocks (eg inside a try..finally, within a begin..end, code that is executed by an if or for statement) are to be indented 2 spaces

Spacing

Blank lines:

• Always consider as being before a declaration (aside from unit name at the beginning)

• No blank lines within a declaration (i.e. record, class, method)

• No multiple blank lines

Spaces within lines:

• Only one space should separate identifiers, operators etc, i.e. no aligning of declarations using multiple spaces

• Operators must be separated by a space, eg:

  LHeight := (Screen.Height / 2) + (Height / 2);

• No spaces before or after parentheses, except for before a constant array declaration, or the values in an enumeration declaration, where the values are split over more than one line, e.g:

// Constant array declaration:
const
  LanguageValues: array[TVersionLanguage] of Word = (
    $0401, $0402, $0403, $0404, $0405, $0406, $0407, $0408, $0409, $040A, $040B, $040C, $040D, $040E, $040F, $0410, $0411, $0412, $0413, $0414, 
    $0415, $0416, $0417, $0418, $0419, $041A, $041B, $041C, $041D, $041E, $041F, $0420, $0421, $0804, $0807, $0809, $080A, $080C, $0810, $0813, 
    $0814, $0816, $081A, $0C0C, $100C, $0000
  );

// Enumeration declaration:
type
  TVersionLanguage = (Arabic, Bulgarian, Catalan, TraditionalChinese, Czech, Danish, German, Greek, USEnglish, CastilianSpanish, Finnish, French,
    Hebrew, Hungarian, Icelandic, Italian, Japanese, Korean, Dutch, NorwegianBokmel, Polish, BrazilianPortuguese, RhaetoRomanic, Romanian, Russian,
    CroatoSerbian, Slovak, Albanian, Swedish, Thai, Turkish, Urdu, Bahasa, SimplifiedChinese, SwissGerman, UKEnglish, MexicanSpanish, BelgianFrench,
    SwissItalian, BelgianDutch, NorwegianNynorsk, Portuguese, SerboCroatian, CanadianFrench, SwissFrench, Unknown
  );

Statements

All statements, where possible, shall be terminated by a semicolon, even where the compiler will allow no semicolon.

Code Blocks

The begin in a begin..end code block is to start on a new line.

Structured Statements

if/while statements:

Where the conditional code of an if/while statement spans more than one line, even if it is only one statement, the conditional code is to be enclosed in a begin..end block. The begin is to be aligned to the if. An if following the else in another if statement is to appear immediately after the else. e.g.

  if SomeCondition then 
  begin 
    DoSomething; 
    DoSomeMore; 
  end 
  else if SomeOtherCondition then 
    DoSomethingElse;

repeat..until statements:

The code inside a repeat..until statement is to be indented 2 spaces

case statements:

• Each case condition is to start on a new line, and be indented 2 spaces in relation to the case keyword

• Code blocks for the case condition that span more than one line are to be enclosed in a begin..end block, which is aligned to the condition, e.g:

  case Key of 
    vkUp: 
    begin 
      DoSomething; 
      DoSomethingElse;
    end; 
    vkDown: 
      DoAnotherThing; 
  else 
    DoSomethingCompletelyDifferent; 
  end;

Documentation and comments

XML Documentation

Where the intention of a method may be unclear, XML Documentation may be used to clarify the intention. The documentation must immediately precede the method declaration, and must contain at least the summary tag.

Comments

Comments should be used sparingly, and should help to clarify why the code is doing a certain thing. Comments should avoid stating what is obvious from reading the code.

Comments must appear on their own lines (i.e. not on the same line as code), and must be used for clarifying what the code is intended for. Comments must use the double slash style, i.e. //

Code must not be commented out completely unless it is part of work in progress, i.e. any code intended for promotion that is commented out must be removed, because the code would already exist in the source control history.

⚠️ **GitHub.com Fallback** ⚠️