Using Namespaces with Delphi
Go Up to Programs and Units Index
In Delphi, a unit is the basic container for types. In Delphi, a namespace is a container of Delphi units.
Unlike traditional Delphi units, namespaces can be nested to form a containment hierarchy. Nested namespaces provide a way to organize identifiers and types, and are used to disambiguate types with the same name. Since they are a container for Delphi units, namespaces may also be used to differentiate between units of the same name, that reside in different packages.
For example, the class MyClass in MyNameSpace, is different from the class MyClass in YourNamespace.
This topic describes the following:
- Project default namespaces, and namespace declaration.
- Namespace search scope.
- Using namespaces in Delphi units.
In RAD Studio, a project file (program, library, or package) implicitly introduces its own namespace, called the project default namespace. A unit may be a member of the project default namespace, or it may explicitly declare itself to be a member of a different namespace. In either case, a unit declares its namespace membership in its unit header. For example, consider the following explicit namespace declaration:
First, notice that the parts of namespaces are separated by dots. Namespaces do not introduce new symbols for the identifiers between the dots; the dots are part of the unit name. The source file name for this example is MyCompany.MyWidgets.MyUnit.pas, and the compiled output file name is MyCompany.MyWidgets.MyUnit.dcu.
Second, notice that the dots imply the conceptual nesting, or containment, of one namespace within another. The example above declares the unit MyUnit to be a member of the MyWidgets namespace, which itself is contained in the MyCompany namespace. Again, it should be noted that this containment is for documentation purposes only.
A project default namespace declares a namespace for all of the units in the project. Consider the following declarations:
Program MyCompany.Programs.MyProgram; Library MyCompany.Libs.MyLibrary; Package MyCompany.Packages.MyPackage;
These statements establish the project default namespace for the program, library, and package, respectively. The namespace is determined by removing the rightmost identifier (and dot) from the declaration.
A unit that omits an explicit namespace is called a generic unit. A generic unit automatically becomes a member of the project default namespace. Given the preceding program declaration, the following unit declaration would cause the compiler to treat MyUnit as a member of the MyCompany.Programs namespace.
The project default namespace does not affect the name of the Delphi source file for a generic unit. In the preceding example, the Delphi source file name would be MyUnit.pas. The same rule applies for the dcu file name. The resulting dcu file in the current example would be MyUnit.dcu.
Namespace strings are not case-sensitive. The compiler considers two namespaces that differ only in case to be equivalent. However, the compiler does preserve the case of a namespace, and will use the preserved casing in output file names, error messages, and RTTI unit identifiers. RTTI for class and type names will include the full namespace specification.
A unit must declare the other units on which it depends. The compiler must search these units for identifiers. For units in explicit namespaces the search scope is already known, but for generic units, the compiler must establish a namespace search scope.
Consider the following unit and uses declarations:
unit MyCompany.ProjectX.ProgramY.MyUnit1; uses MyCompany.Libs.Unit2, Unit3, Unit4;
These declarations establish MyUnit1 as a member of the MyCompany.ProjectX.ProgramY namespace. MyUnit1 depends on three other units: MyCompany.Libs.Unit2, and the generic units, Unit3, and Unit4. The compiler can resolve identifier names in Unit2, since the uses clause specified the fully qualified unit name. To resolve identifier names in Unit3 and Unit4, the compiler must establish a namespace search order.
Namespace search order
Search locations can come from three possible sources: compiler options, the project default namespace, and the current unit's namespace.
The compiler resolves identifier names in the following order:
- The current unit namespace (if any)
- The project default namespace (if any)
- Namespaces specified by compiler options.
A namespace search example
The following example project and unit files demonstrate the namespace search order:
// Project file declarations... program MyCompany.ProjectX.ProgramY; // Unit source file declaration... unit MyCompany.ProjectX.ProgramY.MyUnit1;
Given this program example, the compiler would search namespaces in the following order:
- Namespaces specified by compiler options.
Note that if the current unit is generic (i.e. it does not have an explicit namespace declaration in its unit statement), then resolution begins with the project default namespace.
Delphi's uses clause brings a module into the context of the current unit. The uses clause must either refer to a module by its fully qualified name (i.e. including the full namespace specification), or by its generic name, thereby relying on the namespace resolution mechanisms to locate the unit.
Fully qualified unit names
The following example demonstrates the uses clause with namespaces:
unit MyCompany.Libs.MyUnit1； uses MyCompany.Libs.Unit2, // Fully qualified name. UnitX; // Generic name.
Once a module has been brought into context, source code can refer to identifiers within that module either by the unqualified name, or by the fully qualified name (if necessary, to disambiguate identifiers with the same name in different units). The following Writeln statements are equivalent:
uses MyCompany.Libs.Unit2; begin Writeln(MyCompany.Libs.Unit2.SomeString); Writeln(SomeString); end.
A fully qualified identifier must include the full namespace specification. In the preceding example, it would be an error to refer to SomeString using only a portion of the namespace:
Writeln(Unit2.SomeString); // ERROR! Writeln(Libs.Unit2.SomeString); // ERROR! Writeln(MyCompany.Libs.Unit2.SomeString); // Correct. Writeln(SomeString); // Correct.
It is also an error to refer to only a portion of a namespace in the uses clause. There is no mechanism to import all units and symbols in a namespace. The following code does not import all units and symbols in the MyCompany namespace:
uses MyCompany; // ERROR!
This restriction also applies to the with-do statement. The following will produce a compiler error:
with MyCompany.Libs do // ERROR!
Multiple units can belong to the same namespace, if the unit declarations refer to the same namespace. For example, one can create two files, unit1.pas and unit2.pas, with the following unit declarations:
// in file 'unit1.pas' unit MyCompany.ProjectX.ProgramY.Unit1 // in file 'unit2.pas' unit MyCompany.ProjectX.ProgramY.Unit2
In this example, the namespace MyCompany.ProjectX.ProgramY logically contains all of the interface symbols from unit1.pas and unit2.pas.
Symbol names in a namespace must be unique, across all units in the namespace. In the example above, it is an error for Unit1 and Unit2 to both define a global interface symbol named mySymbol.
The individual units aggregated in a namespace are not available to source code unless the individual units are explicitly used in the file's uses clause. In other words, if a source file uses only the namespace, then fully qualified identifier expressions referring to a symbol in a unit in that namespace must use the namespace name, not just the name of the unit that defines that symbol.
A uses clause may refer to a namespace as well as individual units within that namespace. In this case, a fully qualified expression referring to a symbol from a specific unit listed in the uses clause may be referred to using the actual unit name or the fully-qualified name (including namespace and unit name) for the qualifier. The two forms of reference are identical and refer to the same symbol.