marc rohloff . com

Articles | Files | Down the Rabbit Hole Personal | Leading | .NET | Delphi

Namespaces in Delphi 2005

Delphi

 

This is a slightly updated version of the article I wrote for BDN.

 

You can also read a German translation at Delphi-Source.de or Delphi-Praxis.

 

 

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­.Con­trols­.Base­.TControl or Sample.Con­trols.Listview.­TList­view.

 

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.­TCon­trol.

 

 

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:

 

Default Namespace Setting in 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:

 

<[email protected] 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.­List­view.­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 implement­ation 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.

 



About | Contact - ©2005 Marc Rohloff