Namespaces and References
One of the conceptual differences between Delphi and Oxygene is the handling of References and Namespaces in Oxygene.
In Delphi, every source module (except the main program) starts with the keyword and a unique name (Delphi's recent addition of half-baked namespaces confuses this matter a bit, but let's leave that aside for now). That unique name must match the filename on disk, and it becomes the name that this unit will later be "used" as.
Elsewhere in the project (or in other projects making use of the unit), the name of the unit must be specified in the clause for types and other items defined in the unit to be accessible. In order to be able to "use" a unit, its source file (or a precompiled compiler-version-specific .dcu file of the same name) must either be listed as part of the project, or be found in one of several Search Path settings.
For example, you might create a file called
Helpers.pas, and start it with the line
unit Helpers. In other files of your project, you will include
uses ..., Helpers, ...;, in order to make the items declared in that unit available to the following code.
As a Delphi developer, all of the above is obvious and second nature to you, but reiterating it will help to put in perspective how similar things are achieved in Oxygene.
Unlike Delphi, an Oxygene project does not commonly directly include or import external source files, such as code from shared libraries, third party libraries or even the core platform/OS classes. Instead, external types and other entities are obtained via References to other libraries. These can either be pre-compiled binary libraries (.dll files on .NET, .jar files on Java and .fx and .a Files on Cocoa, or references to other projects that are opened as part of the same Solution (a Solution is comparable to a Project Group in Delphi parlance) that compiles to such a library.
Simply by virtue of being referenced, any (public) types and global entities exposed by the library are automatically available to all code in the project that references the library.
For example, let's assume we have a library called
Helpers which contains several types, named
Bar and so on. If we create a new project and add a Reference to Helpers.dll (or Helpers.jar or Helpers.fx, depending on the platform), our code can make immediate use of these types, simply by referring to them via their (full) name. For instance, it could new up a copy of the
Foo class, simply by writing
Once the reference is made, the code in the project doesn't need to worry what library (let alone what source file) the types it needs have been declared in. There is no need for a clause simply to access the types, or to cause the library to be linked against.
uses clauses still have their – pardon the pun – use, as we will see in the next section.
In Oxygene, types can be contained in namespaces, and while it is possible to declare a type to be namespace-less, that is very rarely (if ever) done, so for all intents and purposes, one could say that just about all types are part of a namespace.
In Delphi, every source file starts with the
unit keyword, in Oxygene every source file starts with
namespace, instead (the
unit keyword is also supported in Delphi Compatibility Mode, but it will behave identical to
namespace is also (optionally) followed by a name. That name is what will be considered the default namespace for the file. The default namespace influences two aspects of the code inside the file:
- By default, all types declared in the file will become part of this namespace.
- All types from this namespace — no matter where they are defined — will be "in scope" for the file.
What does this mean, exactly? Let's have a closer look.
By default, all types declared in the file will become part of this namespace. This means that if we define a type as follows:
namespace MyCompany.MyProject ... type MyClass = class ... end;
MyClass class will automatically be considered part of the
MyCompany.MyProject namespace. Its so-called fully qualified name will be
MyCompany.MyProject.MyClass. And this full name is how the class can be referred to everywhere.
All types from this namespace will be "in scope" for the file. This means that if we add a second (or more) file to our project and also begin it with
namespace MyCompany.MyProject, this file will be part of the same namespace, and all types from that namespace will be in scope. As such, we can refer to the above class simply as
MyClass — because it is in the same namespace.
The nice thing is that you can add as many files to your project as you need and they can all share the same namespace. This way, you never have to worry about adding items to the uses clause just to access classes from within your own project. All your types are automatically in scope in all source files (of the same namespace).
Of course, while it is common for small to medium projects to just use a single namespace, you are also free to declare different namespaces across your project in order to better partition parts of your project — for example you could have a
MyCompany.MyProject namespace for the bulk of your project, but a
MyCompany.MyProject.MySubSystem namespace for a certain sub-system of the project.
Regardless of namespace, all types declared in a project will be available all throughout the project (unless they are marked for
unit level visibility only) by their full name, and all types marked as
public will also be available outside of the project (i.e. when you are creating a library that will be referenced from other projects).
Namespaces and References
The key point to remember is that namespaces work totally independent of references, as discussed above. Namespaces are logical groupings of types into a common, well, name space, and they work across references. You could have several libraries that all contain classes belonging to the same namespace. Conversely, you could have a single library reference that contains classes spread across several namespaces.
It is also perfectly fine for your own main project to declare types that are part of a namespace that is also used by referenced libraries (although you should take care to only define types in namespaces you control, not in System namespaces such as
System.* on .NET or
android.* on Java).
We learned above that any type available to a project (whether from inside the project or via references) will be accessible anywhere via its fully qualified name. So, for example, simply by referencing
System.Xml.Linq.dll (on .NET), you can access the
XDocument via its full name:
namespace MyCompany.MyProject ... begin var x := new System.Xml.Linq.XDocument(...);
(Note that in this example, the name of the .dll and the namespace of the
XDocument class are identical. This is common practice, but does not really indicate a direct link between the two. A library can have any name, and contain any namespaces.)
However, it can become tedious to always have to refer to classes by their fully qualified name, and that is where clauses come into play.
Syntactically similar to Delphi, in Oxygene the
uses clause can (optionally) be present in both the
implementation section of a source file, and it can provide a comma-separated list of namespaces that will be considered in scope for the remainder of the file.
For example, if a lot of places in the code above were to refer to types from
System.Xml.Linq, the code could be simplified like this:
namespace MyCompany.MyProject ... uses System.Xml.Linq; ... begin var x := new XDocument(...);
XDocument can be accessed directly and without its full name.
Some Special Considerations for
- In addition to listing individual namespaces, the
usesclause also allows the asterisk character as a wildcard to include a namespace and all its sub-namespaces. For example,
uses System.Xml.*would add
System.Xml.Linqto the scope, but also
System.Xmland any other sub-namespace.
Certain System namespaces will be in scope by default and do not manually need to be listed in the uses clause for their types to be accessible by their short name.
RemObjects.Elements.Systemnamespace contains compiler-intrinsic types, such as Integer, System Functions and other elements, and is always in scope.
- On .NET, the
Systemnamespace contains many core classes, such as String and Object, and is always in scope.
- On Java, the
java.langnamespaces contain many core classes, such as String and Object, and are always in scope.
- On Cocoa, the
rtl.*namespace contains the C runtime library, core types and many core C-based APIs and is always in scope.