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.
References
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 Foo
, 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 Foo()
.
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.
However, uses
clauses still have their – pardon the pun – use, as we will see in the next section.
Namespaces
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
.
Just like unit
, 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;
then the 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 system.*
, java.*
and android.*
on Java).
uses
Clauses
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 interface
and 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(...);
and now XDocument
can be accessed directly and without its full name.
Some Special Considerations for uses
Clauses
- In addition to listing individual namespaces, the
uses
clause also allows the asterisk character as a wildcard to include a namespace and all its sub-namespaces. For example,uses System.Xml.*
would addSystem.Xml.Linq
to the scope, but alsoSystem.Xml
and 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.
- The
RemObjects.Elements.System
namespace contains compiler-intrinsic types, such as Integer, System Functions and other elements, and is always in scope. - On .NET, the
System
namespace contains many core classes, such as String and Object, and is always in scope. - On Java, the
system
andjava.lang
namespaces 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.
- The