Logo

Marco Cantù's
Essential Pascal

Kapitel 11
Programme und Units

Delphi-Applikationen machen intensiven Gebrauch von Units bzw. Programmodulen. Tatsächlich waren Units die Grundlage der Modularität in der Sprache, bevor Klassen eingeführt wurden. In einer Delphi-Applikation korrespondiert jedes Formular mit einer dahinterliegenden Unit. Wenn Sie ein neues Formular zu einem Projekt hinzufügen (über den entsprechenden Schalter in der Werkzeugleiste oder das Menükommando Datei | Neues Formular) so erzeugt Delphi eine neue Unit, welche die Klasse für das neue Formular definiert.

Units

Obwohl jedes Formular in einer Unit definiert ist, so ist der Umkehrschluss nicht richtig. Units müssen nicht unbedingt Formulare definieren; sie können auch einfach eine Sammlung von Routinen definieren und zur Verfügung stellen. Wenn Sie das Menükommando Datei | Neu... und anschließend das Icon Unit auf der Seite Neu der Objektgalerie auswählen, dann fügen Sie dem aktuellen Projekt eine neue leere Unit hinzu. Diese leere Unit enthält folgenden Code, der die Unit in verschiedene Abschnitte einteilt:

unit Unit1;

interface

implementation

end.

Das Konzept einer Unit ist einfach. Eine Unit hat einen eindeutigen Namen, der dem Dateinamen entspricht, einen interface-Abschnitt, der deklariert, was für andere Units sichtbar sein soll und einen implementation-Abschnitt mit dem eigentlichen Code und weiteren verborgenen Deklarationen. Abschließend besitzt eine Unit einen optionalen initialization-Abschnitt, der einen bestimmten Startup-Code beinhaltet und dann ausgeführt wird, wenn das Programm in den Hauptspeicher geladen wird; weiterhin kann die Unit einen optionalen finalization-Abschnitt besitzen, welcher bei Beendigung des Programms ausgeführt wird.

Die allgemeine Struktur einer Unit, mit allen möglichen Abschnitten, ist demnach:

unit unitName;

interface

// andere Units, die wir referenzieren müssen
uses
  A, B, C;

// exportierte Type-Definitionen
type
  newType = TypeDefinition;

// exportierte Konstanten
const
  Zero = 0;

// globale Variablen
var
  Total: Integer;

// die Liste der exportierten Funktionen und Prozeduren
procedure MyProc;

implementation

uses
  D, E;

// verborgene globale Variablen
var
  PartialTotal: Integer;

// alle exportierten funktionen müssen implementiert werden
procedure MyProc;
begin
  // ... Code der Prozedur MyProc
end;

initialization
  // Optionaler Initialisierungsteil

finalization
  // Optionaler Aufräum-Code

end.

Die uses-Clausel am Anfang des interface-Abschnitts zeigt an, auf welche anderen Units wir im interface-Abschnitt einen Zugriff benötigen. Das beinhaltet solche Units, welche Datentypen definieren, die wir für die Definition weiterer Datentypen verwenden, wie das die verwendeten Komponenten tun, die wir innerhalb eines Formulars verwenden.

Die zweite uses-Clausel, am Anfang des implementation-Abschnitts, zeigt weitere Units an, auf die wir nur einen Zugriff im Implementierungs-Code benötigen. Wenn Sie für den Code von Routinen und Methoden auf andere Units verweisen müssen, so sollten sie diese Elemente in dieser zweiten uses-Clausel einfügen anstatt in der ersten. Alle Units, auf die Sie verweisen, müssen im Verzeichnis des Projekts oder in einem Verzeichnis des Suchpfades vorhanden sein (Sie können den Suchpfad für ein Projekt auf der Seite Verzeichnisse/Bedingungen des Dialogs Projektoptionen einstellen).

C++ Programmierer sollten beachten, dass die uses-Clauses nicht mit einer Include-Direktive korrespondiert. Der Effekt einer uses-Clausel besteht darin, nur den vorcompilierten Interface-Teil der aufgeführten Units zu importieren. Der Implementierungsteil einer Unit wird nur dann berücksichtigt, wenn diese Unit compiliert wird. Units, auf die man verweisen möchte, können dabei sowohl im Quellcode-Format (PAS) als auch im compilierten Format (DCU) vorliegen, wobei die Compilierung mit der selben Version von Delphi durchgeführt werden muss.

Das Interface einer Unit deklariert eine Reihe von unterschiedlichen Elementen, einschließlich Prozeduren, Funktionen, globale Variablen und Datentypen. In Delphi-Applicationen werden die Datentypen wahrscheinlich am häufigsten benutzt. Delphi erstellt jedesmal automatisch einen neuen Class-Datentyp, wenn Sie ein Formular erzeugen. Jedoch ist das Bereitstellen von Formular-Definitionen ist nicht der einzige Verwendungszweck von Units. Sie können weiterhin traditionelle Units verwenden, mit Funktionen und Prozeduren, und Sie können mit Klassen verwenden, die keine Beziehung zu Formularen oder anderen visuellen Elementen besitzen.

Units und der Gültigkeitsbereich

In Pascal bilden Units den Schlüssel für Kapselung und Sichtbarkeit, und sie sind wahrscheinlich noch bedeutender als die Schlüsselworte private und public einer Klasse. (Wie wir im nächsten Kapitel noch sehen werden, betrifft das Schlüsselwort private tatsächlich den Gültigeitsbereich der Unit, in der die Klasse deklariert wurde.) Den Gültigkeitsbereich eines Bezeichners (wie z.B. einer Variable, Prozedur, Funktion oder eines Datentyps) bildet der Code-Abschnitt, in dem dieser Bezeichner verwendet werden kann. Die Grundregel besagt dabei, dass ein Bezeichner nur innerhalb seines Gültigkeitsbereichs verwendbar ist, d.h. nur innerhalb des Abschnitts, in welchem er deklariert wurde. Sie können keinen Bezeichner ausserhalb seines Gültigkeitsbereichs verwenden. Hierzu einige Beispiele.

Sämtliche Deklarationen im interface-Abschnitt einer Units sind aus jedem Programmteil verwendbar, welches die Unit in seiner uses-clausel verwendet. Variablen von Formularklassen werden auf die gleiche Art deklariert, so dass Sie auf ein Formular (und seine public Felder, Methoden, Eigenschaften und Komponenten) innerhalb des Codes jedes beliebigen anderen Formulars darauf zugreifen können. Allerdings ist es ein schlechter Programmierstil, alles global zu vereinbaren. Neben dem allgemeinen Problem des Speicherverbrauchs, macht die Verwendung von globalen Variablen ein Programme schlechter wartbar und modifizierbar. Zusammengefasst sollten Sie so wenig globale Variablen wie möglich verwenden.

Units als Namensbereiche

Die uses-Clausel bildet die Standardtechnik, um auf den Gültigkeitsbereich einer anderen Unit zuzugreifen. An dieser Stelle können Sie auf die Deklarationen dieser Unit zugreifen. Aber es kann passieren, dass zwei Units, die Sie verwenden, den gleichen Bezeichner deklarieren; d.h. Sie könnten zwei unterschiedliche Klassen oder Routinen mit dem selben Namen haben.

In diesem Fall können Sie einfach den Unit-Namen als Präfix für den Namen des Typs oder der Routine verwenden, in der diese deklariert sind. Z.B. können Sie auf die Prozedur ComputeTotal verweisen, die in einer Unit Totals deklariert ist, in der Form Totals.ComputeTotale. Das sollte nicht sehr häufig notwendig sein, da Sie ja strengstens darauf hingewiesen wurden, niemals zwei unterschiedliche Dinge innerhalb eines Programms mit dem selben Namen zu versehen.

Wenn Sie jedoch in die VCL-Bibliothek und die Windows-Dateien sehen, so werden Sie feststellen, dass einige Delphi-Funktionen den selben Name (aber i.A. unterschiedliche Parameter) wie einige WindowsAPI-Funktionen haben, die ebenfalls in Delphi verfügbar sind. Ein Beispiel dafür ist die Prozedur Beep.

Erzeugen Sie ein neues Delphi-Programm, fügen einen Schalter hinzu und schreiben Sie folgenden Code:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Beep;
end;

Sobald Sie den Schalter drücken, hören Sie einen kurzen Signalton. Jetzt gehen Sie zur uses-Clausel der Unit und ändern den Code von dieser Version:

uses
  Windows, Messages, SysUtils, Classes, ...

in diese sehr ähnliche Version (verschieben Sie einfach die SysUtils Unit vor die Windows Unit):

uses
  SysUtils, Windows, Messages, Classes, ...

Wenn Sie nun versuchen, diesen Code zu compilieren, so werden sie folgenden Compiler-Fehler erhalten: "Nicht genügend wirkliche Parameter". Das Problem liegt darin, dass die Windows-Unit eine weitere Funktion Beep deklariert, allerdings mit zwei Parametern. Allgemeiner ausgedrückt, es kann passieren, dass die genutzten Deklarationen der ersten Unit, durch gleichnamige Deklarationen von weiteren Units verdeckt werden. Die sichere Lösung für diese Problem ist jedoch ganz einfach:

procedure TForm1.Button1Click(Sender: TObject);
begin
  SysUtils.Beep;
end;

Dieser Code lässt sich unabhängig von der Reihenfolge der verwendeten Units in der uses-Clausel compilieren. Es gibt noch einige weitere Namenskonflikte in Delphi, einfach dadurch, weil Delphi's Code im allgemeinen innerhalb von Klassenmethoden implementiert ist. Das Vorliegen von zwei Methoden mit dem selben Namen innerhalb von zwei unterschiedlichen Klassen erzeugt jedoch keine Probleme. Diese entstehen nur bei globalen Routinen.

Units und Programme

Eine Delphi-Applikation besteht aus zwei Arten von Quellcode-Dateien: eine oder mehrere Units und eine Programmdatei. Die Units können als sekundäre Dateien betrachtet werden, welche durch den Hauptteil der Applikation, dem Programm referenziert werden. In der Theorie ist diese Aussage wahr. In der Praxis hingegen ist die Programmdatei normalerweise eine automatisch erzeugte Datei, welche nur eine untergeordnete Rolle spielt. Sie ist einfach dafür notwendig, um das Programm zu starten und das Hauptformular aufzurufen. Der Code einer Programmdatei, oder auch Delphi Projektdatei (DPR) genannt, kann sowohl manuell als auch durch die Verwendung des Projekt-Managers sowie über einige der Projektoptionen, die sich auf das Application-Objekt und die Formulare beziehen, bearbeitet werden

Die Struktur einer Programmdatei ist gewöhnlich viel einfacher als die Struktur der Units. Hier ist beispielhaft der Quellcode einer Programmdatei angegeben:

program Project1;

uses
  Forms,
  Unit1 in ‘Unit1.PAS’ {Form1DateForm};

begin
  Application.Initialize;
  Application.CreateForm (TForm1, Form1);
  Application.Run;
end.

Wie Sie sehen können, enthält sie einfach eine uses-Clausel und das Hauptprogramm der Applikation, eingeschlossen in die Schlüsselwörter begin und end. Die uses-Clausel des Programms ist besonders wichtig, da sie für das Compilieren und Linken der Anwendung verantwortlich ist.

Zusammenfassung

Zumindest bis jetzt ist dieses Kapitel über die Struktur einer in Delphi oder einer der letzten Versionen von Turbo Pascal geschriebenen Pascal-Applikation das letzte in diesem Buch. Kommentare oder Anmerkungen hierzu sind stets willkommen. Schicken Sie einfach eine email.

Wenn Sie nach dieser Einführung in die Sprache Pascal weiter in die objektorientierten Elemente von Object Pascal mit Delphi eintauchen wollen, so verweise ich Sie auf mein veröffentlichtes Buch Mastering Delphi 5 (Sybex, 1999). Für weitere Informationen über dieses und weitere Bücher von mir (und auch anderen Autoren) besuchen Sie meine Web-Site unter http://www.marcocantu.com/. Auf dieser Site finden Sie auch die aktuelle Version dieses Buchs sowie die angegebenen Beispiele.

Zurück zur Titelseite

© Copyright Marco Cantù, Wintech Italia Srl 1995-2000
© Copyright der deutschen Übersetzung Immo Wache, 2000