This document is mainly to provide Delphi developers with a source code writing standard, as well as a naming standard for programs and files, so that they can have a consistent format to follow when programming. This way, each programmer writes code that can be understood by others.
Indentation means two spaces between each level. Don't place tab characters in source code. This is because the width of the tab character varies with different settings and code management utilities (printing, documentation, version control, etc.).
By using the Tools|Environment menu, on the General page of the Environment Options dialog box, uncheck the Use Tab Character and Optional Fill check boxes so that tab characters are not saved.
Margins are set to 80 characters. Source code generally does not exceed the margin by writing a word, but this rule is more flexible. Whenever possible, statements longer than one line should be wrapped with commas or operators. After a line break, it should be indented by two characters.
The begin statement must be on a line by itself. For example, the first line below is incorrect, but the second line is correct:
for i:=0 to 10 do begin // Wrong, begin and for are on the same line
for i:=0 to 10 do // Yes, begin on another line
begin
A special case of this rule is when begin is part of an else statement, for example:
if some statement = then
begin
. . .
end
else begin
Some Other Statement;
end;
Note: The end statement is always on a separate line. When begin is not part of an else statement, the corresponding end statement is indented by the same amount as the begin statement.
We usually use "{...}" type block comments. The previous "(*...*)" type block comments are used to temporarily comment out unused code. Starting from Delphi 2, "//" is supported. Line comments, if you decide not to support versions below Delphi 2.0, you can use the "//" comment.
There is no space between the opening bracket and the next character. Likewise, there is no space between the closing bracket and the previous character. The following example demonstrates correct and incorrect whitespace.
CallPRoc( Aparameter ); // Error!
CallProc(Aparameter); // Correct!
Do not include extra parentheses in statements. In source code, parentheses are used only when really needed. The following examples demonstrate correct and incorrect usage:
if (I=42) then // Error, the parentheses are redundant
if (I=42) or (J=42) then // Correct, brackets must be used
Reserved words and keywords in the Object Pascal language are always entirely lowercase. The following is a list of Delphi 5 reserved words:
and | array | as | asm |
begin | case | class | const |
constructor | destructor | dispinterface | div |
do | downto | else | end |
except | exports | file | finalization |
finally | for | function | goto |
if | implementation | in | inherited |
initialization | inline | interface | is |
label | library | mod | nil |
not | object | of | or |
out | packed | procedure | program |
property | raise | record | repeat |
resourcestring | set | shl | shr |
string | then | threadvar | to |
try | type | unit | until |
uses | var | while | with |
xor | private | protected | public |
published | automated |
Procedure names should begin with an uppercase letter and be staggered to increase readability. The following is an incorrect way of writing:
procedure thisisapoorlyformattedroutinename;
Just change it to this:
procedure ThisIsMuchMoreReadableRoutineName;
Whenever possible, parameters of the same type should be grouped together:
procedure Foo(Param1,Param2,Param3:Imteger;Param4:string);
The order of formal parameters mainly depends on the register calling rules. The most commonly used parameter should be the first parameter, arranged from left to right in order of frequency of use. Input parameters precede output parameters. Parameters with a large range should be placed before parameters with a small range. For example:
SomeProc(aPlanet, aContinent, aCountry, aState, aCity).
Some are exceptions. For example, during event processing, the Sender parameter of type TObject is often the first parameter to be passed.
To prevent parameters of record, array, short string, or interface type from being modified by the procedure, the formal parameter should be marked Const. In this way, the compiler will generate code in the most efficient way, ensuring that the passed parameters are immutable.
If other types of parameters are not expected to be modified by the procedure, they can also be marked Const. Although this has no impact on efficiency, it gives the caller of the procedure more information.
Local variables are used inside the procedure. If necessary, the variables should be initialized immediately at the entry of the procedure. Local AnsiString type variables are automatically initialized to an empty string, local interface and dispinterface type variables are automatically initialized to nil, and local Variant and OleVariant type variables are automatically initialized to Unassigned.
The use of global variables is generally discouraged. However, sometimes it is needed. Even so, global variables should be restricted to the environments where they are needed. For example, a global variable may be global only to the implementation part of the unit.
Global data that will be used by many units should be moved to a common unit and used by all objects. Global data can be directly initialized to a value when declared. Note that all global variables are automatically zero-initialized, so do not initialize global variables to null values such as 0, nil, or Unassigned. Zero-initialized global variables take up no space in the .EXE file. Zero-initialized data is stored in a virtual data segment, and the virtual data segment only allocates memory when the application starts. Non-zero initialized global data takes up space in the .EXE file.
Type identifiers are reserved words and should be all lowercase. Win32 API types are often all-capitalized and follow the rules for specific type names in Windows.pas or other API units. For other variable names, the first letter should be capitalized, and other letters should be in alternating case. Here are some examples:
var
MyString: string; // reserved words
WindowsHandle: HWND; // Win32 API type
I: Integer; //Type identifier introduced in the System unit
The use of the Real type is discouraged as it is reserved only for compatibility with older Pascal code. Normally, Double should be used for floating point numbers. Double can be optimized by the processor and is a standard data format defined by IEEE. Extend can be used when a larger range than Double provides is required. Extend is an Intel-specific type and is not supported by Java. When the physical number of bytes of the floating point variable is important (perhaps using a different language to write the DLL), Single should be used.
It is generally not recommended to use Variant and OleVariant. However, these two types are necessary for programming when the data type is known only at runtime (often in COM and database applications). When doing COM programming such as automating ActiveX controls, you should use OleVariant; for non-COM programming, you should use Variant. This is because Variant can effectively save Delphi's native strings, while OleVariant converts all strings to OLE strings (ie, WideChar strings) and has no reference counting function.
In an if/then/else statement, the most likely execution case should be placed in the then clause, and the less likely case should be placed in the else clause. To avoid many if statements, use case statements instead. If there are more than 5 levels, don't use if statements. Please use a clearer method instead. Don't use extra parentheses in if statements.
If there are multiple conditions to be tested in an if statement, they should be ordered from right to left in order of computational complexity. This allows the code to take full advantage of the compiler's short-circuit estimation logic. For example, if Condition1 is faster than Condition2 and Condition2 is faster than Condition3, the if statement should generally be constructed like this:
if Condition1 and Condition2 and Condition3 then
If there is a high chance that Condition3 is False, using short-circuit estimation logic, we can also put Condition3 at the front:
if Condition3 and Condition1 and Condition2 then
The constants for each case in the case statement should be arranged in numerical or alphabetical order. The action statement for each situation should be short and usually no more than 4 - 5 lines of code. If the action is too complex, the code should be placed in a separate procedure or function. The else clause of the Case statement is only used for default cases or error detection.
Case statements follow normal indentation and naming rules.
It is recommended not to use the Exit procedure to exit the while loop. If necessary, a loop condition should be used to exit the loop. All code that initializes the while loop should be located before the while entry and should not be separated by irrelevant statements. Any auxiliary work for the business should be carried out immediately after the cycle.
If the number of loops is determined, a for statement should be used instead of a while statement.
The repeat statement is similar to a while loop and follows the same rules.
The with statement should be used with caution. Avoid overuse of with statements, especially when using multiple objects or records within a with statement. For example:
with Record1,Record2 do
These situations can easily confuse programmers and make debugging difficult.
The with statement also follows this chapter's rules for naming and indentation.
Exception handling is mainly used to correct errors and protect resources. This means that wherever resources are allocated, try...finally must be used to ensure that the resources are released. However, exceptions are made if resources are allocated/released in the initial/final part of the unit or in the constructor/destructor of the object.
Where possible, each resource allocation should match a try...finally structure. For example, the following code may cause an error:
SomeClass1 := TSomeClass.Create;
SomeClass2 := TSomeClass.Create;
try
{ do some code }
finally
SomeClass1.Free;
SomeClass2.Free;
end;
A safe solution for the above resource allocation is:
SomeClass1 := TSomeClass.Create;
try
SomeClass2 := TSomeClass.Create;
try
{ do some code }
finally
SomeClass2.Free;
end;
finally
SomeClass1.Free;
end;
If you want to perform some task when an exception occurs, you can use try...except. Usually, there is no need to use try...except to simply display an error message, because the application object can do this automatically based on the context. If you want to activate default exception handling in the clause, you can trigger the exception again.
Using try...except with an else clause is discouraged because this will block all exceptions, including exceptions that you are not prepared to handle.
Procedure and function names should be meaningful. It is best to prefix the name of the process of performing an action with the verb that expresses the action. For example:
procedure FormatHardDrive;
The name of the procedure for setting input parameter values should be prefixed with Set, for example:
procedure SetUserName;
The name of the procedure for obtaining a value should be prefixed with Get, for example:
function GetUserName:string;
The name of all formal parameters should express its purpose. If appropriate, the name of the formal parameter is preferably prefixed with the letter a, for example:
procedure SomeProc(aUserName:string; aUserAge:integer);
The prefix a is necessary when the parameter name has the same name as a class attribute or field.
When two units contain procedures with the same name, if the procedure is called, the procedure in the unit that appears later in the Uses clause is actually called. To avoid this, add the desired unit name before the method name, for example:
SysUtils.FindClose(SR);
or Windows.FindClose(Handle);
The name of a variable should express its purpose. Loop control variables are often single letters, such as I, J, or K. You can also use a more meaningful name, such as UserIndex. Boolean variable names must clearly indicate the meaning of the True and False values.
Local variables follow the naming rules of other variables.
Global variables begin with a capital letter "G" and follow the naming rules of other variables.
The enumeration type name must represent the purpose of the enumeration. The T character must be prefixed before the name to indicate that this is a data type. The prefix of the identifier list of the enumeration type should contain 2 - 3 lowercase characters to associate with each other. For example:
TSongType=(stRock, stClassical, stCountry, stAlternative, stHeavyMetal, stRB);
The name of a variable instance of an enumeration type is the same as the type, but without the prefix T. You can also give the variable a more special name, such as: FavoriteSongTypel, FavoriteSongType2, etc.
The array type name should express the purpose of the array. Type names must be prefixed with the letter "T". If you want to declare a pointer to an array type, you must prefix it with the letter P and declare it before the type declaration. For example:
type
PCycleArray = ^TCycleArray;
TCycleArray=array[1..100] of integer;
In fact, a variable instance of an array type has the same name as the type, but without the "T" prefix.
The record type name should express the purpose of the record. Type names must be prefixed with the letter T. If you want to declare a pointer to a record type, you must prefix it with the letter P and declare it before the type declaration. For example:
type
PEmployee = ^TEmployee;
TEmployee=record
EmployeeName: string;
EmployeeRate: Double;
end;
The name of the class should express the purpose of the class. Generally, the letter "T" should be added before the class name. If it is an interface class, then "I" should be added before the class name. The class name of the error exception class should be added with "E", and the class reference type (Class-reference type) should be added before the class name. Add "Class" after the name. For example:
type
TCustomer = class(TObject);
ICustomer = interface;
TCustomerClass = class of TCustomer
ECustomerException = class(Exception);
The instance name of a class is usually the same as the class name, without the prefix "T".
var
Customer: TCustomer;
Note: For the naming of components, see "Component Types".
The naming of fields follows the same rules as variables, except that the prefix F is added to indicate that this is a field.
All fields must be private. If you want to access a field outside the scope of a class, you can do so with the help of class attributes.
Naming methods follows the same rules as procedures and functions.
Static methods should be used when you do not want a method to be overridden by derived classes.
When you want a method to be overridden by derived classes, you should use virtual methods. If a class method is to be used directly or indirectly by multiple derived classes, dynamic methods (dynamic) should be used. For example, if a class contains a frequently overridden method and has 100 derived classes, the method should be defined as dynamic, which can reduce memory overhead.
If a class is going to create instances, don't use abstract methods. Abstract methods can only be used in base classes that never create instances.
All property access methods should be defined in the private or protected part of the class. Property access methods follow the same rules as procedures and functions. Methods used for reading should be prefixed with "Get", methods used for writing should be prefixed with "Set", and have a parameter called Value whose type is the same as the type of the property. For example:
TSomeClass = class(TObject)
private
fsomeField: Integer;
protected
function GetSomeField: Integer;
procedure SetSomeField(Value: Integer);
public
property SomeField: Integer read GetSomeField write SetSomeField;
end;
Although not required, it is recommended that you use write access methods to access properties representing private fields.
Properties serve as accessors to private fields and follow the same naming rules as fields, except without the F prefix. Property names should be nouns, not verbs. Properties are data and methods are actions. Array property names should be plural, while general properties should be singular.
The naming of components is similar to the naming of classes, except that when it conflicts with other component names, you can add a 3-character prefix to identify a company, individual, or other entity. For example, a clock component can be declared like this:
TddgClock = class(TComponent)
Note that the three characters of the prefix must be lowercase.
The name of the component instance should be able to describe its actual meaning. The naming convention here uses a modified Hungarian prefix naming convention. The reason for using a prefix instead of a suffix is that it is easier to search for the component's name in the Object Inspector and Code Explorer than to search for the component's type. In this standard, the component instance name consists of two parts: the prefix and the attribute identifier.
The prefix of a component is mostly an abbreviation of the component type. See component prefixes in the table below:
Component class name | Component prefix |
TActionList, TAction represents the list item of the action | act |
TButton, TSpeedButton, TBitBtn and other button classes | btn |
TCheckBox, TDBCheckBox and other check boxes | chk |
TRadioButton radio button class | rdo |
TToolBar toolbar | tb |
All main menu classes of TMainMenu | mm |
All menu item classes of TMainMenuItem | mi |
All pop-up menu classes of TPopupMenu | pm |
All pop-up menu item classes of TPopupMenuItem | pmi |
TLabel, TStaticText and other label classes used for display | lbl |
TPanel and other panel classes | nnl |
TPageControl and other page control classes | pgc |
TEdit, TMaskEdit and other single-line edit box classes | edt |
TMemo, TRichEdit and other multi-line edit box classes | mmo |
TDrawGrid, TStringGrid and other grid classes | grd |
TAnimate and other animation classes | ani |
TImageList and other image list classes | il |
TImage and other image classes | img |
TChart chart class | cht |
TComboBox, TDBComboBox and other drop-down list box classes | cbo |
TListBox, TDBList and other list box classes | lst |
TTreeView | tv |
TListView | lv |
THotKey | hk |
TSplitter and other delimiter classes | spt |
All dialog component classes such as TOpenDialog | dlg |
All data table classes such as TTable | tbl |
All SQL query components such as TQuery | qry |
TClientDataSetAll client data set elements | cds |
TDataSource | ds |
TDatabase | db |
TSockConnection, TDCOMConnection and other connection component classes | con |
TQuickRep, TFastReport and other report component classes | rpt |
TDDEClientConv, TDDEClientItem and other DDE component classes | dde |
All calendar classes such as TMonthCalendar | cal |
TGroupBox and other control classes | grp |
As shown above, the component type prefix comes from analyzing the type properties that describe the component. Typically, the following rules describe how to define a component type prefix:
Note: The prefix of a component is to indicate the type of component, whether it is a button, a label, etc., so there is no need to create a component prefix for each special component class. For example, the component prefix of TMyButton is still btn.
The component property identification name is a description of the component's intent. For example, a TButton component instance used to close a form could be named btnClose. An instance of the edit name component can be named edName.
The name of the form or dialog type should express the purpose of the form, prefixed with "Tfrm" for a form or "Tdlg" for a dialog box, followed by a descriptive name. For example, the About form type name is:
TfrmAbout = class(TForm)
The type name of the main form is:
TfrmMain = class(TForm)
The type name of the customer login form is:
TfrmCustomerEntry = class(TForm)
The type name of the login dialog box is:
TdlgLogin = class(TForm)
The name of the form instance is the same as the corresponding type name, but without the prefix T. For example, the name of the form type and instance mentioned earlier is:
Type name | Instance name |
TfrmAbout | frmAbout |
TfrmMain | frmMain |
TfrmCustomerEntry | frmCustomerEntry |
TdlgLogin | dlgLogin |
Unless there are special reasons, only the main form is automatically generated. All other forms must be removed from the automatically generated list in the Project Options dialog box. For further information, see the following sections.
All form units should contain instantiation functions for creating, setting up, modal displaying, and releasing the form. This function will return the mode result returned by the form. Parameters passed to this function follow the rules for parameter passing. The reason for encapsulation like this is to facilitate code reuse and maintenance.
The form's variables should be removed from the unit and defined as local variables in the form instantiation function (note that this requires removing the form from the automatically generated list in the Project Options dialog box. Please see the previous content.
For example, the following unit file demonstrates the GetUserData instantiation function.
Unit UserDataFrm;
Interface
Uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
Type
TfrmUserData = class(TForm)
edtUserName: TEdit;
edtUserID: TEdit;
private
{Private declarations}
public
{Public declarations}
end;
function GetUserData(var aUserName: String; var aUserID: Integer): Word;
implementation
{$R *.DFM}
function GetUserData(var aUserName: String;var aUserID: Integer): Word;
var
frmUserData: TfrmUserData;
begin
frmUserData := TfrmUserData.Create(Application);
frmUserData.Caption:='Getting User Data';
Result : = frmUserData.ShowModal;
if Result=mrOK then
begin
aUserName := frmUserData.edtUserName.Text;
aUserID := StrToInt(frmUserData.edtUserID.Text);
end;
finally
frmUserData.Free;
end;
end;
End.
If a form structure is too complex, it must be divided into a main form frame and several sub-form frames embedded in the main form frame. like:
TfrmMainFrame: TfrmInfoFrame,TfrmEditorFrame
The main purpose of using form frames is to solve the problem of interface and code reuse, and to improve the cohesion of unit code (after division, each form frame is an independent unit), thereby improving the quality of software engineering. You must extract interface-related code (reusable) and application-related code (non-reusable).
The data module type name should express its purpose and be prefixed with "Tdm" followed by a descriptive name. For example, the type name of the Customer data module is:
TdmCustomer = class(TDataModule)
The type name of the Orders data module is:
TdmOrder = class(TDataModule)
The name of a data module instance should be the same as the corresponding type name, but without the T prefix. For example, the previous data module type and instance name are as follows:
Type name | Instance name |
TdmCustomer | dmCustomer |
TdmOrder | dmOrder |
It is recommended to use structured file header information in all source files, project files and unit files. A file header should contain at least the following information:
{
Copyright @ Year by Authors
}
Project file names should be descriptive. For example, the project name of "The Delphi 5 Developer's Guide Bug Manager" is DDGBugs.dpr, and the name of a system information program is SysInfo.dpr.
The name of the form file should express the purpose of the form and have the suffix Frm. For example, the file name of the About form is AboutFrm.dfm, and the file name of the main form is MainFrm.dfm.
The name of the data module file should express the role of the data module and have a DM suffix. For example, the file name of the Customers data module is CustomersDM.dfm.
The name of the remote data module file should express the purpose of the remote data module. Add the RDM suffix after the name. For example, the file for the Customers remote data module is called CustomersRDM.dfm.
Unit names should be descriptive. For example, the application's main form unit is called MaimFrm.pas.
The Uses clause of the Interface section should contain only the units required by that section. Do not include unit names that may be added automatically by Delphi. The Uses clause of the Implementation part should only contain the units needed for this part, and no extra units.
The Interface section should contain only declarations of types, variables, procedures, and functions that need to be accessed by external units. Furthermore, these declarations should precede the Implementation section.
The Implementation part includes the implementation of private types, variables, procedures and functions of this unit.
Do not place time-consuming code in the Initialization section. Otherwise, it will cause the application to start very slowly.
Make sure to release all resources allocated in the Initialization section.
The name of the form unit file is the same as the corresponding form name, just change the prefix to the suffix. For example, the unit name of the About form is AboutFrm.pas. The unit file name of the main form is MainFrm.pas.
The name of the data module unit file is the same as the corresponding data module name. For example, the name of the data module unit is CustomersDM.pas.
The name of a general unit should express its purpose and should be prefixed with "u". For example, the name of a practical debugging tool unit is uDebugUtilities.pas, and the name of the unit containing global variables is uCustomerGlobals.pas.
Note that unit names must be unique within a project. Common unit names cannot have the same name.
Component cells should be placed in separate paths to indicate that they are the cells that define the component. They are generally not placed in the same path as the project. Unit file names should express their contents.
Note, see "Naming Standards for Component Types" for more information about component naming standards.
A component cell can contain only one primary component, which is the component that appears on the component palette. Other auxiliary components or objects can also be included in the same unit.
The component registration process should be moved out of the components unit and placed in a separate unit. This registration unit is used to register all components, property editors, component editors, wizards, etc.
Component registration should be done in the design package. Therefore, registration units should be included in the design-time package rather than the run-time package. It is recommended that the registration unit be named like this:
xxxReg.pas
Among them, the xxx character prefix is used to identify the component package name or company, individual, or other entity. For example, the registration unit is named xxxReg.pas.
The runtime package should contain only the required units. Those elements of the property editor and component editor should be placed in the design-time package. Registration units should also be included in the design phase package.
Package naming follows the following pattern:
dcliiiDescvvCn.pkg —Design package
iiiDescvvCn.pkg —runtime package
Among them, iii represents a 2-3 character prefix, which is used to identify companies, individuals or other things that need to be identified. It is optional; Desc represents a short description of the control package; vv represents the version number of the package. You can choose according to your needs. ;The prefix "dcl" indicates a design-time package, without this prefix indicates a run-time package; the letter "Cn" indicates the compiler type and compiler version number, such as: Delphi5=D5, Delphi4=D4, CBuilder3=C3....
Note that lib or std in the package name indicates whether this is a design-time package or a run-time package respectively. For example:
dclrbStdComPSD5.pkg —Delphi 5 design-time package
rbStdCompsD5.pkg —Delphi 5 runtime package
Although most code automatic formatting tools can help you rearrange the source program format and update the capitalization of reserved words and identifiers, it is best to do this before using version control. If you have already used version control, it is recommended that you Don't use automatic code formatting tools easily. Even if there is one more space, the version control tool will think that the line has been modified, thus causing changes to program management.