Namespaces in Delphi

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Programme und Units - Index


Wichtig: RAD Studio unterstützt jetzt zusätzlich zum Namespace- oder Unit-Namen einen Unit-Gültigkeitsbereichsnamen bzw. ein Präfix. Damit ein Name als voll qualifiziert gilt, muss er den Unit-Gültigkeitsbereichsnamen enthalten. Weitere Einzelheiten finden Sie unter Unit-Gültigkeitsbereichsnamen.

In Delphi ist eine Unit der Basis-Container für Typen. In Delphi dient ein Namespace als Container für Delphi-Units.

Im Gegensatz zu herkömmlichen Delphi-Units können Namespaces verschachtelt werden, um hierarchisch strukturierte Enthalten-Beziehungen zu definieren. Verschachtelte Namespaces dienen zur Organisation von Bezeichnern und Typen und sorgen gleichzeitig dafür, dass es zwischen Typen mit identischen Namen nicht zu Namenskonflikten kommt. Als Container für Delphi-Units können Namespaces auch verwendet werden, um zwischen gleichnamigen Units zu unterscheiden, die sich in verschiedenen Packages befinden.

So werden beispielsweise MyClass in MyNameSpace und MyClass in YourNamespace als unterschiedliche Klassen behandelt.

In diesem Thema wird Folgendes beschrieben:

  • Projekt-Standard-Namespace und Namespace-Deklaration
  • Suchbereich für Namespaces
  • Namespaces in Delphi-Units

Deklarieren von Namespaces

In RAD Studio führt eine Projektdatei (Programm, Bibliothek oder Package) implizit ihren eigenen Namespace ein: den Projekt-Standard-Namespace. Eine Unit kann Teil dieses Projekt-Standard-Namespace sein oder so deklariert werden, dass sie ein Element eines anderen Namespace ist. Die Namespace-Zugehörigkeit einer Unit wird in jedem Fall im unit-Kopf festgelegt. Sehen Sie sich hierzu folgende explizite Namespace-Deklaration an:

unit MyCompany.MyWidgets.MyUnit;

Die Bestandteile des Namespace sind durch Punkte voneinander getrennt. Namespaces führen keine neuen Symbole für die Bezeichner zwischen den Punkten ein. Die Punkte sind Teil des Unit-Namens. Im obigen Beispiel ist MyCompany.MyWidgets.MyUnit.pas der Name der Quelldatei. Nach dem Compilieren lautet der Dateiname MyCompany.MyWidgets.MyUnit.dcu.

Die Punkte geben außerdem die Enthalten-Beziehung der Namespaces an, d. h. wie die Namespaces ineinander verschachtelt sind. In der obigen Deklaration ist MyUnit ein Element des Namespace MyWidgets, der wiederum im Namespace MyCompany enthalten ist. Die Enthalten-Beziehung dient lediglich Dokumentationszwecken.

Der Projekt-Standard-Namespace legt den Namespace für alle Units des Projekts fest. Sehen Sie sich hierzu die folgenden Deklarationen an:

Program MyCompany.Programs.MyProgram;
Library MyCompany.Libs.MyLibrary;
Package MyCompany.Packages.MyPackage;

Diese Anweisungen richten den Projekt-Standard-Namespace für ein Programm (Program), eine Bibliothek (Library) und ein Package (Package) ein. Der Namespace ergibt sich, wenn der Bezeichner auf der rechten Seite (einschließlich Punkt) aus der Deklaration entfernt wird.

Eine Unit ohne expliziten Namespace wird als generische Unit bezeichnet. Eine generische Unit ist automatisch ein Element des Projekt-Standard-Namespace. Ausgehend von der obigen Program-Deklaration veranlasst die folgende Unit-Deklaration, dass der Compiler MyUnit als Element des Namespace MyCompany.Programs behandelt:

unit MyUnit;

Der Projekt-Standard-Namespace hat bei generischen Units keinen Einfluss auf den Namen der Delphi-Quelldatei. Ausgehend von obiger Deklaration würde dieser Name MyUnit.pas lauten. Dieselbe Regel gilt für den Namen der dcu-Datei. Die resultierende dcu-Datei würde für das aktuelle Beispiel MyUnit.dcu heißen.

Bei Namespace-Strings wird nicht zwischen Groß- und Kleinschreibung unterschieden. Namespaces, die sich nur hinsichtlich der Groß-/Kleinschreibung unterscheiden, werden vom Compiler als identisch betrachtet. Der Compiler behält die Schreibweise des Namespace jedoch bei und verwendet sie in Namen von Ausgabedateien, Fehlermeldungen und Unit-Bezeichnern in Laufzeittypinformationen. Klassen- und Typnamen in Laufzeittypinformationen werden immer mit der vollständigen Namespace-Spezifikation angegeben.

Suchbereich für Namespaces

Jede Unit muss alle anderen Units deklarieren, von denen sie abhängig ist. Der Compiler muss diese Units nach Bezeichnern durchsuchen. Für Units in expliziten Namespaces ist der Suchbereich bekannt. Für generische Units muss der Compiler jedoch einen Namespace-Suchbereich bestimmen.

Sehen Sie sich hierzu die folgenden unit- und uses-Deklarationen an:

unit MyCompany.ProjectX.ProgramY.MyUnit1;
uses MyCompany.Libs.Unit2, Unit3, Unit4;

MyUnit1 ist ein Element des Namespace MyCompany.ProjectX.ProgramY und von drei anderen Units abhängig: von MyCompany.Libs.Unit2 sowie den generischen Units Unit3 und Unit4. Bezeichnernamen in Unit2 können vom Compiler problemlos aufgelöst werden, da die uses-Klausel den voll qualifizierten Unit-Namen enthält. Um Bezeichnernamen in Unit3 und Unit4 aufzulösen, muss der Compiler jedoch die Suchreihenfolge für die Namespaces bestimmen.

Suchreihenfolge für Namespaces

Suchpositionen können in drei Quellen definiert sein: In den Compiler-Optionen, im Projekt-Standard-Namespace und im Namespace der aktuellen Unit.

Der Compiler verwendet bei der Auflösung von Bezeichnernamen die folgende Suchreihenfolge:

  1. Aktueller Unit-Namespace (falls vorhanden)
  2. Projekt-Standard-Namespace (falls vorhanden)
  3. Über Compiler-Optionen angegebene Namespaces

Beispiel für die Suche in Namespaces

Das folgende Beispielprojekt und die Unit-Dateien zeigen die Suchreihenfolge in Namespaces:

// Project file declarations...
program MyCompany.ProjectX.ProgramY;

// Unit source file declaration...
unit MyCompany.ProjectX.ProgramY.MyUnit1;

Unter diesen Voraussetzungen durchsucht der Compiler die Namespaces in der folgenden Reihenfolge:

  1. MyCompany.ProjectX.ProgramY
  2. MyCompany.ProjectX
  3. Über Compiler-Optionen angegebene Namespaces

Wenn die aktuelle Unit generisch ist (also keine explizite Namespace-Deklaration in der unit-Anweisung enthält), beginnt die Auflösung beim Projekt-Standard-Namespace.

Verwenden von Namespaces

In Delphi gelangt ein Modul über die uses-Klausel in den Kontext der aktuellen Unit. Das Modul muss in der uses-Klausel entweder mit dem voll qualifizierten Namen (einschließlich der kompletten Namespace-Spezifikation) oder über seinen generischen Namen referenziert werden. Im letzteren Fall kommt der Mechanismus zur Namespace-Auflösung zum Einsatz, um die Unit zu lokalisieren.

Voll qualifizierte Unit-Namen

Das folgende Beispiel zeigt eine uses-Klausel mit Namespaces:

unit MyCompany.Libs.MyUnit1;
uses MyCompany.Libs.Unit2,  // Fully qualified name.
  UnitX;                   // Generic name.

Sobald sich ein Modul im Kontext befindet, können darin enthaltene Bezeichner über den nicht qualifizierten Namen oder über den voll qualifizierten Namen (falls mehrere Units einen Bezeichner gleichen Namens enthalten) referenziert werden. Die folgenden Writeln-Anweisungen sind äquivalent:

uses MyCompany.Libs.Unit2;

begin
  Writeln(MyCompany.Libs.Unit2.SomeString);
  Writeln(SomeString);
end.

Ein voll qualifizierter Bezeichner muss die komplette Namespace-Spezifikation enthalten. Deshalb würde ein Bezug auf SomeString, der nur einen Teil des Namespace enthält, zu einem Fehler führen:

Writeln(Unit2.SomeString);       // ERROR!
Writeln(Libs.Unit2.SomeString);  // ERROR!
Writeln(MyCompany.Libs.Unit2.SomeString);      // Correct.
Writeln(SomeString);                           // Correct.

Unzulässig ist es auch, nur einen Teil eines Namespace in der uses-Klausel anzugeben. Es gibt keinen Mechanismus, der alle Units und Symbole in einem Namespace importiert. Die folgende Anweisung importiert beispielsweise nicht alle Units und Symbole im Namespace MyCompany:

uses MyCompany;   // ERROR!

Diese Einschränkung gilt auch für die with-do-Anweisung. Folgender Code führt zu einem Compiler-Fehler:

with MyCompany.Libs do    // ERROR!

Multi-Unit-Namespaces

Wenn die Unit-Deklarationen auf denselben Namespace verweisen, können mehrere Units zu demselben Namespace gehören. Beispielsweise könnten Sie zwei Dateien, unit1.pas und unit2.pas, mit den folgenden Unit-Deklarationen erstellen:

// in file 'unit1.pas'
unit MyCompany.ProjectX.ProgramY.Unit1

// in file 'unit2.pas'
unit MyCompany.ProjectX.ProgramY.Unit2

In diesem Beispiel enthält der Namespace MyCompany.ProjectX.ProgramY alle interface-Symbole aus unit1.pas und unit2.pas.

Symbolnamen in einem Namespace müssen für alle Units in dem Namespace eindeutig sein. Im obigen Beispiel wäre es ein Fehler, wenn für Unit1 und Unit2 ein globales Interface-Symbol namens mySymbol definiert wäre.

Die einzelnen in einem Namespace gruppierten Units stehen dem Quelltext nur zur Verfügung, wenn die einzelnen Units explizit in der uses-Klausel der Datei enthalten sind. Das heißt: Wenn eine Quelldatei den Namespace verwendet, dann müssen die voll qualifizierten Bezeichnerausdrücke, die ein Symbol in einer Unit dieses Namespace referenzieren, den Namespace-Namen verwenden und nicht nur den Namen der Unit, die dieses Symbol definiert.

Eine uses-Klausel kann auf einen Namespace als auch auf einzelne Units in diesem Namespace verweisen. In diesem Fall kann ein voll qualifizierter Ausdruck, der auf ein Symbol in einer bestimmten, in der uses-Klausel aufgeführten Unit verweist, mit dem Unit-Namen oder dem voll qualifizierten Namespace-Namen (Namespace-Name und Unit-Name) für den Qualifizierer referenziert werden. Die beiden Referenzierungsarten sind identisch und verweisen auf dasselbe Symbol.

Hinweis: Die explizite Verwendung einer Unit in der uses-Klausel funktioniert nur, wenn Sie Quelltext- oder dcu-Dateien compilieren. Wenn die Namespace-Units in eine Assembly compiliert werden und die Assembly von dem Projekt anstatt von den einzelnen Units referenziert wird, dann schlägt der Quelltext, der eine Unit in dem Namespace explizit referenziert, fehl.

Siehe auch