Delphi Clinic C++Builder Gate Training & Consultancy Delphi Notes Weblog Dr.Bob's Webshop
Dr.Bob's Delphi Notes Dr.Bob's Delphi Clinics Dr.Bob's Delphi Courseware Manuals
 Dr.Bob Examines... #69
See Also: other Dr.Bob Examines columns or Delphi articles

Delphi for .NET Units and Namespaces
In this paper, I will explain how Delphi for .NET treat units with respect to namespaces. I will also cover a small but irritating unit/namespace importing issue in Delphi 2005, plus a workaround to stop it from nagging you (or at least me).

At the time of writing, there are two different versions of Delphi for .NET, namely Delphi 8 for .NET and Delphi 2005 (and a third one, codenamed DeXter will be released before the end of this year, perhaps resulting in an update of this article).

Example Assembly
As example project, let's assume we have a single assembly (package) project eBob42.NET (resulting in assembly eBob42.NET.dll), with the following units as part of the assembly:

Units with dots in their name are often called "dotted", by the way, and this feature is supported since Delphi 7 (although namespaces are only a .NET issue).

The contents of the eBob42.Test unit will be shown later, introducing some more subtle issues. First, let's see how these units are respondible for the namespaces in the eBob42.NET.dll assembly.
Delphi 8 for .NET
With Delphi 8 for .NET, Borland made a first attempt at defining the "connection" between unit names and namespaces. In Delphi 8 for .NET, a unit name is equivalent to the namespace, so for the above example eBob42.NET project, we have the following namespaces: eBob42.Navigator and eBob42.Test.
If you look at the screenshot of Borland's Reflection tool (analysing the eBob42.NET.dll assembly compiled with Delphi 8 for .NET), you'll see that each of these namespaces has a special class called "Unit". This is the class where the code from the initialization and finalization sections of each unit is placed.
What it boils down to, is that if you put each class in its own unit (which I think is a good coding style), then using Delphi 8 for .NET each class will be placed in its own namespace (which is not exactly a good result - especially if you want to register a component prefix for ASP.NET controls, which has to be unique for each namespace). The physical structure of your assembly (the units it is made from) are reflected as logical structure as well, which is not always helpful for users of your assemblies.

Delphi 2005
With Delphi 2005, Borland resolved the issue that each unit results in a new namespace. The new rules to determine the namespace, based on a unit name, are as follows:

If we apply these rules on our example (which uses dotted unit names), then unit eBob42.Navigator results in namespace eBob42, and so does unit eBob42.Test, so these two units now share the same namespace eBob42!
The physical structure of your assembly (the units it is made from) no longer determine the logical structure of my assemblies. Instead, I can ensure that namespaces define the logical structure, and each namespace can consist of one or more units containing the types, classes and other items from that namespace.

Imported Property Types
Although the Delphi 2005 namespace solution is perfect for most cases, it introduced one little issue with respect to custom types defined in our assemblies. If a Delphi for .NET client imports the assembly, then we - as Delphi developer - must always add the unit name to the uses clause in order to use the type (and we can specify either the type itself, or fully qualify it with the unit name in front of it). From any other .NET development environment, it's the namespace that must be imported (and not the unit name), and the type can be prefixed with the namespace only (a unit name doesn't make sense in C# or any other .NET development environment apart from Delphi for .NET).

This almost works. Unfortunately, when you import and use a custom type from a Delphi for .NET assembly back in a Delphi for .NET application, then apart from the unit name, you will also get the namespace in your uses clause.
TO illustrate the problem, consider the following code for my unit eBob42.Test:

  unit eBob42.Test;
  interface
  uses
    System.ComponentModel;

  type
    TestEnum = (Test1,Test2,Test3);

    TestComponent42 = class(Component)
    private
      FTest: TestEnum;
    public
      constructor Create;
      procedure set_Test(const Value: TestEnum);
    published
      [DefaultValue(Test1)]
      property Test: TestEnum read FTest write set_Test;
    end;

  implementation

  constructor TestComponent42.Create;
  begin
    inherited Create;
    Test := test1;
  end;

  procedure TestComponent42.set_Test(const Value: TestEnum);
  begin
    FTest := Value;
  end;

  end.
I can compile the package, and install the TestComponent42 in the Delphi 2005 Tool Palette using the Component | Installed .NET Components dialog. I can then use the component in a WinForm or ASP.NET WebForm application. This will add the unit name eBob42.Test to the uses clause. This is good.
Now, change the value of the default property from Test1 to Test2 for example. This will also add the eBob42 namespace name to the uses clause. This is not good at all, and will not compile.

You can manually remove the eBob42 entry from the uses clause again, but it's a problem/bug that it's generated in the first place (and it gets boring rather quickly that you have to manually remove it from the uses clause every time).

The only workaround that I've found at this time, is to make sure that the item "eBob42" in the uses clause of the using Delphi for .NET application doesn't result in a compiler error, by making sure this unit actually exists in the original assembly.
In other words: just add an empty unit with the name of the offending namespace, in this case unit eBob42. This will make sure that the reference to "eBob42" in the uses clause will not result in a compiler error. Something to keep in mind when building Delphi 2005 assemblies (with custom property types) that you want other Delphi 2005 developers to use...

Latest News: when I checked this problem in the version of DeXter I was allowed to use for my Borland DevCon session, I could see that the problem has been solved in Delphi 2006 by Borland. Thanks!


This webpage © 2005-2010 by Bob Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved.