To make Delphi code friendlier towards other .NET languages
Borland has changed the way Delphi 2005 produces namespaces. This will make it
easier for code written in other environments, like C# or Visual Basic, to use
assemblies written in Delphi Pascal. The great thing is that Borland has done
this without changing the language and all your previous code should still
compile and run just as before. In fact, you normally do not even have to be
concerned about namespace support in Delphi at all.
Executive Summary
•
Namespaces do not affect Delphi for Win32.
•
Namespaces are produced based on the unit name as follows:
•
If a unit name is dotted then the unit name up to the last dot is
used.
•
If it is not dotted then the default namespace is used.
•
If there is no default namespace then the unit name is used.
•
Code converted from Delphi 8 may require some modifications.
•
Always use Packages not Libraries to create shareable assemblies.
•
Delphi will always refer to types by their full unit name.
•
Other languages will need to refer to types using their namespace.
•
ASP.NET declarations, configuration files and .NET reflection always
refer to types by their namespace.
•
If multiple units emit a public type with the same name into the same
namespace, the assembly may be unusable.
|
Since namespaces are a feature of .NET, this article is only
applicable to the Delphi .NET personality and there is no change in the way you
work with Win32 code.
Namespaces in Delphi 8
First, I will review how
units and namespaces worked in Delphi 8. I am going to use these two simple
source files as examples:
Sample.Controls.Base.pas
unit Sample.Controls.Base;
interface
type
TControl = class
end;
implementation
end.
|
Sample.Controls.Listview.pas
unit Sample.Controls.Listview;
interface
type
TListview = class(TControl)
end;
type
TItem = class
end;
implementation
end.
|
If you compile these into a package using Delphi 8 and look
at the resulting assembly using a tool like Reflector you will see that it
contains two namespaces: Sample.Controls.Base and
Sample.Controls.Listview.
Open up the Sample.Controls.Base
namespace and you will notice that it contains two classes. The first is the TControl class while the second is a class named Unit. This special class contains the initialization
and finalization code for the Sample.Controls.Base
unit as well as any procedures and functions defined at the unit level. Similarly,
the Sample.Controls.Listview namespace contains
the TListview and TItem
classes as well as a Unit class for the Sample.Controls.Listview unit.
If you continue adding more units to the package, you will
see that each unit gets its own unique namespace containing all the classes in
the unit as well as an implementation of the Unit
class.
So what was the problem?
Units are much easier to manage and maintain if there are
only a few classes in each of them. However creating libraries with many units
generates assemblies that contain a namespace for each unit. This is fine for
Delphi programmers since the compiler hides the details of the assembly layout
from us, but it is a problem for programmers who want to use our assemblies in
other languages. C# programmers, for example, will have to import each
namespace individually, making their code verbose and your library difficult to
learn. If they want to use fully qualified names, then they will have to type
something like Sample.Controls.Base.TControl
or Sample.Controls.Listview.TListview.
Namespaces in Delphi 2005
Load the package in Delphi 2005 and recompile it, then look
at the resulting assembly again. You will notice that this time it looks
slightly different. It now contains two namespaces Sample.Controls
and Sample.Controls.Units.
If you take a closer look at
the Sample.Controls namespace, you will see that
it contains TControl, TListview and TItem. The compiler has placed all of the controls into
a single namespace!
The Sample.Controls.Units
namespace is similar and contains two classes Base
and Listview. These are the special classes for
each unit that we saw previously. Unlike with Delphi 8, adding more units will
not add new namespaces.
This is much easier for other programmers to use. C#
programmers can now type a single using statement
to access all of the classes. For example, to access a class using its full
name they can write Sample.Controls.TControl.
Now add a third source file to the package:
Sample.Graphics.Screen.pas
unit Sample.Graphics.Screen;
interface
type
TScreen = class
end;
implementation
end.
|
You will see that the assembly now contains a Sample.Graphics namespace and a Sample.Graphics.Units
namespace for the classes and unit we have just defined.
So where does the namespace come from?
The Delphi compiler looks at the name of each unit. If it is
a dotted name then it will remove the part to the left of the last dot and use
the left part as the namespace. In other words, a unit called Sample.Controls.Buttons will emit its classes in the Sample.Controls namespace while a unit called Sample.Controls.Web.DB.Grid will emit its classes
into the Sample.Controls.Web.DB namespace.
For unit names that do not
contain a dot, the compiler will use the Default Namespace from the Project
Options dialog:
Note that there still are issues when using the default namespace feature for packages and you probably shouldn't use them currently.
Lastly, if you there is no value for the default namespace then
the compiler will simply use the unit’s name as the namespace. Therefore, a
unit called Graphics will create a namespace with
exactly the same name, Graphics.
Some Important Things to Remember
As with any change, there are times when the details can
trip you up and you may get unusual errors. Here are three that you need to
remember:
1) Always
Use Packages
Borland has always recommended not using library projects to
create assemblies. In Delphi 2005, the compiler is stricter and will no longer allow
you to compile an application referencing a Delphi ‘library’.
Always use a package project to create .NET assemblies.
2) Referring
to Types by their Fully Qualified Name
Most .NET languages require you to use the namespace to
prefix types or as part of a namespace import (using)
clause. However, Delphi will always use the unit name and never the
namespace to qualify the type. This is true even if you add a reference to a
third-party assembly created with Delphi as the compiler will detect this and
use the appropriate unit names. (Note that when referring to assemblies created
in other languages you should just use the namespace to qualify types.)
This mismatch often creates confusion. A common example is
when using TypeConverters and Designers. For example given a type converter defined
in a Delphi unit:
Sample.Controls.Design.pas
Type
TConverter = class(TypeConverter)
end;
|
To use this type converter you need to use the unit name to
qualify the type:
Uses
Sample.Controls.Design;
type
[TypeConverter(typeof(TConverter))]
TItem = class
end;
|
or
Type
[TypeConverter(typeof(Sample.Controls.Design.TConverter))]
TItem = class
end;
|
Whereas the equivalent C#
code would only require you to use the namespace as the prefix:
using Sample.Controls;
[TypeConverter(typeof(TConverter))]
class TListItem {
}
|
or
[TypeConverter(typeof(Sample.Controls.TConverter))]
class TListItem {
}
|
However, as with anything, there are exceptions to the rule
and there are several situations where you need to use the namespace even
though you are coding in Delphi.
One example is when you are using tags in ASPX, ASCX or ASMX
files. If you create a web page called TWebForm
in the file Application.Pages.WebForm1.pas
then the page tag would be:
<%@Page Language="c#"
Codebehind="WebForm1.pas" Inherits="Application.Pages.TWebForm1"%>
|
Another is when passing type information to .NET in the form
of strings, especially when using reflection based APIs and configuration files:
var c:TypeConverter;
c:=Assembly.CreateInstance('Sample.Controls.TConverter');
|
This slight difference commonly trips people up and is
something you need to be careful of, especially when porting code from Delphi 8
or converting code from other languages.
3) Duplicate
Types
If we use the same name to define types in two units that
have the same namespace then this creates a duplicate type, much like having
two types in a single unit with the same name. As an example, add the following
source file to the package:
Sample.Controls.Treeview.pas
unit Sample.Controls.Treeview;
interface
type
TTreeview = class(TControl)
end;
type
TItem = class
end;
implementation
end.
|
We now have two classes in our package called TItem, one in Sample.Controls.Listview.pas
and the other in Sample.Controls.Treeview.pas.
If we look at the assembly using Reflector
you will notice that the Sample.Controls
namespace contains two types called TItem. This
can occur not only for class types but also for any type supported by Delphi
such as records and enumerations.
To avoid this problem, the compiler renames all types
that are not visible from outside the assembly, in other words everything
defined in the implementation section of a unit.
It does this by appending the unit name to all internal types.
However if there are two public types in the same namespace
with the same name then others might have problems using the assembly. A future
release of the compiler should detect this situation. In the meantime I
recommend checking your executables and assemblies using the peverify tool that ships with the .NET SDK as part of
your testing.
Porting Code from Delphi 8
You can generally port Delphi 8 to Delphi 2005 without making
any changes; however, you should consider the above issues when doing so.
Here are some things you may want to check for:
•
Convert library projects to packages unless you are using unmanaged
exports.
•
Check namespaces used in code, especially in reflection and
configuration files.
•
The IDE should convert ASPX Tags for you but it is always worth checking
them.
•
Check your assemblies for duplicate types using peverify.
Summary
Once you understand how Delphi generates namespaces, Delphi’s
support for them is logical, flexible and very easy to use, without changing
the language. So much so that you will never even know it is there until you
need it. Nevertheless, it makes Delphi assemblies far easier to use for other coders.
The most important point to remember is that a namespace and
a unit name are no longer the same thing. Delphi always uses unit names.