RAD Studio (Common)
|
A Delphi program is constructed from source code modules called units. The units are tied together by a special source code module that contains either the program, library, or package header. Each unit is stored in its own file and compiled separately; compiled units are linked to create an application. RAD Studio introduces hierarchical namespaces, giving you even more flexibility in organizing your units. Namespaces and units allow you to
A complete, executable Delphi application consists of multiple unit modules, all tied together by a single source code module called a project file. In traditional Pascal programming, all source code, including the main program, is stored in .pas files. CodeGear tools use the file extension .dpr to designate the main program source module, while most other source code resides in unit files having the traditional .pas extension. To build a project, the compiler needs the project source file, and either a source file or a compiled unit file for each unit.
The compiler, and hence the IDE, expect to find these three elements in a single project (.dpr) file.
The program heading specifies a name for the executable program. It consists of the reserved word program, followed by a valid identifier, followed by a semicolon. For applications developed using CodeGear tools, the identifier must match the project source file name.
The following example shows the project source file for a program called Editor. Since the program is called Editor, this project file is called Editor.dpr.
program Editor; uses Forms, REAbout, // An "About" box REMain; // Main form {$R *.res} begin Application.Title := 'Text Editor'; Application.CreateForm(TMainForm, MainForm); Application.Run; end.
The first line contains the program heading. The uses clause in this example specifies a dependency on three additional units: Forms, REAbout, and REMain. The $R compiler directive links the project's resource file into the program. Finally, the block of statements between the begin and end keywords are executed when the program runs. The project file, like all Delphi source files, ends with a period (not a semicolon).
Delphi project files are usually short, since most of a program's logic resides in its unit files. A Delphi project file typically contains only enough code to launch the application's main window, and start the event processing loop. Project files are generated and maintained automatically by the IDE, and it is seldom necessary to edit them manually.
In standard Pascal, a program heading can include parameters after the program name:
program Calc(input, output);
CodeGear's Delphi ignores these parameters.
In RAD Studio, a the program heading introduces its own namespace, which is called the project default namespace. This is also true for the library and package headers, when these types of projects are compiled for the .NET platform.
The uses clause lists those units that are incorporated into the program. These units may in turn have uses clauses of their own. For more information on the uses clause within a unit source file, see Unit References and the Uses Clause, below.
The uses clause consists of the keyword uses, followed by a comma delimited list of units the project file directly depends on.
The block contains a simple or structured statement that is executed when the program runs. In most program files, the block consists of a compound statement bracketed between the reserved words begin and end, whose component statements are simply method calls to the project's Application object. Most projects have a global Application variable that holds an instance of TApplication, TWebApplication, or TServiceApplication. The block can also contain declarations of constants, types, variables, procedures, and functions; these declarations must precede the statement part of the block.
A unit consists of types (including classes), constants, variables, and routines (functions and procedures). Each unit is defined in its own source (.pas) file.
A unit file begins with a unit heading, which is followed by the interface keyword. Following the interface keyword, the uses clause specifies a list of unit dependencies. Next comes the implementation section, followed by the optional initialization, and finalization sections. A skeleton unit source file looks like this:
unit Unit1; interface uses // List of unit dependencies goes here... implementation uses // List of unit dependencies goes here... // Implementation of class methods, procedures, and functions goes here... initialization // Unit initialization code goes here... finalization // Unit finalization code goes here... end.
The unit must conclude with the reserved word end followed by a period.
The unit heading specifies the unit's name. It consists of the reserved word unit, followed by a valid identifier, followed by a semicolon. For applications developed using CodeGear tools, the identifier must match the unit file name. Thus, the unit heading
unit MainForm;
would occur in a source file called MainForm.pas, and the file containing the compiled unit would be MainForm.dcu or MainForm.dcuil.
Unit names must be unique within a project. Even if their unit files are in different directories, two units with the same name cannot be used in a single program.
The interface section of a unit begins with the reserved word interface and continues until the beginning of the implementation section. The interface section declares constants, types, variables, procedures, and functions that are available to clients. That is, to other units or programs that wish to use elements from this unit. These entities are called public because code in other units can access them as if they were declared in the unit itself.
The interface declaration of a procedure or function includes only the routine's signature. That is, the routine's name, parameters, and return type (for functions). The block containing executable code for the procedure or function follows in the implementation section. Thus procedure and function declarations in the interface section work like forward declarations.
The interface declaration for a class must include declarations for all class members: fields, properties, procedures, and functions.
The interface section can include its own uses clause, which must appear immediately after the keyword interface.
The implementation section of a unit begins with the reserved word implementation and continues until the beginning of the initialization section or, if there is no initialization section, until the end of the unit. The implementation section defines procedures and functions that are declared in the interface section. Within the implementation section, these procedures and functions may be defined and called in any order. You can omit parameter lists from public procedure and function headings when you define them in the implementation section; but if you include a parameter list, it must match the declaration in the interface section exactly.
In addition to definitions of public procedures and functions, the implementation section can declare constants, types (including classes), variables, procedures, and functions that are private to the unit. That is, unlike the interface section, entities declared in the implementation section are inaccessible to other units.
The implementation section can include its own uses clause, which must appear immediately after the keyword implementation. The identifiers declared within units specified in the implementation section are only available for use within the implementation section itself. You cannot refer to such identifiers in the interface section.
The initialization section is optional. It begins with the reserved word initialization and continues until the beginning of the finalization section or, if there is no finalization section, until the end of the unit. The initialization section contains statements that are executed, in the order in which they appear, on program start-up. So, for example, if you have defined data structures that need to be initialized, you can do this in the initialization section.
For units in the interfaceuses list, the initialization sections of the units used by a client are executed in the order in which the units appear in the client's uses clause.
The finalization section is optional and can appear only in units that have an initialization section. The finalization section begins with the reserved word finalization and continues until the end of the unit. It contains statements that are executed when the main program terminates (unless the Halt procedure is used to terminate the program). Use the finalization section to free resources that are allocated in the initialization section.
Finalization sections are executed in the opposite order from initialization sections. For example, if your application initializes units A, B, and C, in that order, it will finalize them in the order C, B, and A.
Once a unit's initialization code starts to execute, the corresponding finalization section is guaranteed to execute when the application shuts down. The finalization section must therefore be able to handle incompletely initialized data, since, if a runtime error occurs, the initialization code might not execute completely.
A uses clause lists units used by the program, library, or unit in which the clause appears. A uses clause can occur in
The System unit and the SysInit unit are used automatically by every application and cannot be listed explicitly in the uses clause. (System implements routines for file I/O, string handling, floating point operations, dynamic memory allocation, and so forth.) Other standard library units, such as SysUtils, must be explicitly included in the uses clause. In most cases, all necessary units are placed in the uses clause by the IDE, as you add and remove units from your project.
In unit declarations and uses clauses, unit names must match the file names in case. In other contexts (such as qualified identifiers), unit names are case insensitive. To avoid problems with unit references, refer to the unit source file explicitly:
uses MyUnit in "myunit.pas";
If such an explicit reference appears in the project file, other source files can refer to the unit with a simple uses clause that does not need to match case:
uses Myunit;
A uses clause consists of the reserved word uses, followed by one or more comma delimited unit names, followed by a semicolon. Examples:
uses Forms, Main; uses Forms, Main; uses Windows, Messages, SysUtils, Strings, Classes, Unit2, MyUnit;
In the uses clause of a program or library, any unit name may be followed by the reserved word in and the name of a source file, with or without a directory path, in single quotation marks; directory paths can be absolute or relative. Examples:
uses Windows, Messages, SysUtils, Strings in 'C:\Classes\Strings.pas', Classes;
Use the keyword in after a unit name when you need to specify the unit's source file. Since the IDE expects unit names to match the names of the source files in which they reside, there is usually no reason to do this. Using in is necessary only when the location of the source file is unclear, for example when
In the uses clause of a unit, you cannot use in to tell the compiler where to find a source file. Every unit must be in the compiler's search path. Moreover, unit names must match the names of their source files.
The order in which units appear in the uses clause determines the order of their initialization and affects the way identifiers are located by the compiler. If two units declare a variable, constant, type, procedure, or function with the same name, the compiler uses the one from the unit listed last in the uses clause. (To access the identifier from the other unit, you would have to add a qualifier: UnitName.Identifier.)
A uses clause need include only units used directly by the program or unit in which the clause appears. That is, if unit A references constants, types, variables, procedures, or functions that are declared in unit B, then A must use B explicitly. If B in turn references identifiers from unit C, then A is indirectly dependent on C; in this case, C needn't be included in a uses clause in A, but the compiler must still be able to find both B and C in order to process A.
The following example illustrates indirect dependency.
program Prog; uses Unit2; const a = b; // ... unit Unit2; interface uses Unit1; const b = c; // ... unit Unit1; interface const c = 1; // ...
In this example, Prog depends directly on Unit2, which depends directly on Unit1. Hence Prog is indirectly dependent on Unit1. Because Unit1 does not appear in Prog's uses clause, identifiers declared in Unit1 are not available to Prog.
To compile a client module, the compiler needs to locate all units that the client depends on, directly or indirectly. Unless the source code for these units has changed, however, the compiler needs only their .dcu (Win32) or .dcuil (.NET) files, not their source (.pas) files.
When a change is made in the interface section of a unit, other units that depend on the change must be recompiled. But when changes are made only in the implementation or other sections of a unit, dependent units don't have to be recompiled. The compiler tracks these dependencies automatically and recompiles units only when necessary.
When units reference each other directly or indirectly, the units are said to be mutually dependent. Mutual dependencies are allowed as long as there are no circular paths connecting the uses clause of one interface section to the uses clause of another. In other words, starting from the interface section of a unit, it must never be possible to return to that unit by following references through interface sections of other units. For a pattern of mutual dependencies to be valid, each circular reference path must lead through the uses clause of at least one implementation section.
In the simplest case of two mutually dependent units, this means that the units cannot list each other in their interface uses clauses. So the following example leads to a compilation error:
unit Unit1; interface uses Unit2; // ... unit Unit2; interface uses Unit1; // ...
However, the two units can legally reference each other if one of the references is moved to the implementation section:
unit Unit1; interface uses Unit2; // ... unit Unit2; interface //... implementation uses Unit1; // ...
To reduce the chance of circular references, it's a good idea to list units in the implementation uses clause whenever possible. Only when identifiers from another unit are used in the interface section is it necessary to list that unit in the interface uses clause.
Copyright(C) 2008 CodeGear(TM). All Rights Reserved.
|
What do you think about this topic? Send feedback!
|