Prozeduren und Funktionen (Delphi)

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Prozeduren und Funktionen - Index

Dieses Thema enthält Informationen zu folgenden Bereichen:

  • Prozeduren und Funktionen deklarieren
  • Aufrufkonventionen
  • forward- und interface-Deklarationen
  • external-Deklarationen
  • Prozeduren und Funktionen überladen
  • Lokale Deklarationen und verschachtelte Routinen

Allgemeines zu Prozeduren und Funktionen

Prozeduren und Funktionen, die zusammenfassend auch als Routinen bezeichnet werden, sind abgeschlossene Anweisungsblöcke, die von unterschiedlichen Positionen in einem Programm aufgerufen werden können. Eine Funktion ist eine Routine, die nach Ausführung einen Wert zurückgibt. Eine Prozedur ist eine Routine, die keinen Wert zurückgibt.

Funktionsaufrufe können auch in Zuweisungen und Operationen eingesetzt werden, da sie einen Wert zurückgeben. Zum Beispiel:

 I := SomeFunction(X);

Diese Anweisung ruft die Funktion SomeFunction auf und weist das Ergebnis der Variable I zu. Funktionsaufrufe dürfen nicht auf der linken Seite einer Zuweisungsanweisung stehen.

Prozeduraufrufe können als eigenständige Anweisungen eingesetzt werden. Bei Funktionsaufrufen ist dies ebenfalls möglich, wenn die erweiterte Syntax ({$X+}) aktiviert ist. Zum Beispiel:

 DoSomething;

Diese Anweisung ruft die Routine DoSomething auf. Ist DoSomething eine Funktion, wird der Rückgabewert verworfen.

Prozeduren und Funktionen können auch rekursiv aufgerufen werden.

Prozeduren und Funktionen deklarieren

Beim Deklarieren einer Prozedur oder Funktion geben Sie den Namen, die Anzahl und den Typ der Parameter sowie bei Funktionen den Typ des Rückgabewertes an. Dieser Teil der Deklaration wird auch Prototyp, Kopf oder Header genannt. Anschließend schreiben Sie den Code, der beim Aufrufen der Prozedur oder Funktion ausgeführt werden soll. Dieser Teil wird auch als Rumpf oder Block der Routine bezeichnet.

Prozedurdeklarationen

Eine Prozedurdeklaration hat folgende Form:

 procedure procedureName(parameterList); directives;
   localDeclarations;
 begin
   statements
 end;

Prozedurname ist ein beliebiger gültiger Bezeichner, Anweisungen eine Folge von Anweisungen, die beim Aufruf der Prozedur ausgeführt werden. (Parameterliste), Direktiven und LokaleDeklarationen sind optional.

Ein Beispiel für eine Prozedurdeklaration:

 procedure NumString(N: Integer; var S: string);
 var
   V: Integer;
 begin
   V := Abs(N);
   S := '';
   repeat
     S := Chr(V mod 10 + Ord('0')) + S;
     V := V div 10;
   until V = 0;
   if N < 0 then S := '-' + S;
 end;

Nach dieser Deklaration können Sie die Prozedur NumString folgendermaßen aufrufen:

 NumString(17, MyString);

Dieser Prozeduraufruf weist der Variable MyString (es muss sich um eine Variable vom Typ String handeln) den Wert 17 zu.

Im Anweisungsblock einer Prozedur können Sie Variablen und Bezeichner verwenden, die im Abschnitt localDeclarations der Prozedur deklariert wurden. Sie können außerdem die Parameternamen aus der Parameterliste (N und S in diesem Beispiel) verwenden. Die Parameterliste definiert eine Gruppe lokaler Variablen. Aus diesem Grund dürfen im Abschnitt localDeclarations keine gleichlautenden Parameternamen auftauchen. Natürlich können Sie auch alle Bezeichner verwenden, in deren Gültigkeitsbereich die Prozedur deklariert wurde.

Funktionsdeklarationen

Eine Funktionsdeklaration entspricht einer Prozedurdeklaration, definiert aber zusätzlich einen Rückgabetyp und einen Rückgabewert. Funktionsdeklarationen haben folgende Form:

 function functionName(parameterList): returnType; directives;
   localDeclarations;
 begin
   statements
 end;

Funktionsname ist ein beliebiger gültiger Bezeichner, Rückgabetyp ein Typbezeichner, Anweisungen enthält die Folge von Anweisungen, die beim Aufruf der Funktion ausgeführt werden sollen. (Parameterliste), Direktiven; und LokaleDeklarationen; sind optional.

Für den Anweisungsblock der Funktion gelten die Regeln, die bereits für Prozeduren erläutert wurden. Sie können Variablen und andere Bezeichner, die im Abschnitt localDeclarations der Funktion deklariert wurden, die Parameternamen aus der Parameterliste und alle Bezeichner verwenden, in deren Gültigkeitsbereich die Funktion deklariert wurde. Der Funktionsname dient als spezielle Variable, die wie die vordefinierte Variable Result den Rückgabewert der Funktion enthält.

Wenn die erweiterte Syntax ({$X+}) aktiviert ist, wird Result implizit in jeder Funktion deklariert. Sie dürfen diese Variable deshalb nicht manuell deklarieren.

Zum Beispiel:

 function WF: Integer;
 begin
   WF := 17;
 end;

Diese Deklaration definiert eine Konstantenfunktion namens WF, die keine Parameter entgegennimmt und immer den Integerwert 17 zurückgibt. Diese Deklaration ist zur folgenden äquivalent:

 function WF: Integer;
 begin
   Result := 17;
 end;

Das nächste Beispiel enthält eine etwas komplexere Funktionsdeklaration:

 function Max(A: array of Real; N: Integer): Real;
 var
   X: Real;
   I: Integer;
 begin
   X := A[0];
   for I := 1 to N - 1 do
     if X < A[I] then X := A[I];
   Max := X;
 end;

Sie können der Variable Result oder dem Funktionsnamen im Anweisungsblock mehrmals einen Wert zuweisen. Die zugewiesenen Werte müssen jedoch dem deklarierten Rückgabetyp entsprechen. Sobald die Ausführung der Funktion beendet wird, bildet der Wert, der zuletzt der Variable Result oder dem Funktionsnamen zugewiesen wurde, den Rückgabewert der Funktion. Zum Beispiel:

 function Power(X: Real; Y: Integer): Real;
 var
   I: Integer;
 begin
   Result := 1.0;
   I := Y;
   while I > 0 do
    begin
     if Odd(I) then Result := Result * X;
     I := I div 2;
     X := Sqr(X);
    end;
 end;

Result und der Funktionsname repräsentieren immer denselben Wert. Deshalb gibt die Deklaration

 function MyFunction: Integer;
 begin
   MyFunction := 5;
   Result := Result * 2;
   MyFunction := Result + 1;
 end;

den Wert 11 zurück. Result ist jedoch nicht vollständig mit dem Funktionsnamen austauchbar. Wenn der Funktionsname auf der linken Seite einer Zuweisungsanweisung angegeben ist, verwendet der Compiler den Funktionsnamen als Variable (wie Result) zur Aufzeichnung des Rückgabewerts. Taucht der Funktionsname dagegen an einer anderen Stelle im Anweisungsblock auf, wird er vom Compiler als rekursiver Aufruf der Funktion interpretiert. Result kann als Variable in Operationen eingesetzt werden, beispielsweise bei Typkonvertierungen, in Konstruktoren, Indizes und in Aufrufen anderer Routinen.

Wenn die Ausführung einer Funktion beendet wird, bevor Result oder dem Funktionsnamen ein Wert zugewiesen wurde, ist der Rückgabewert der Funktion nicht definiert.

Aufrufkonventionen

Wenn Sie eine Prozedur oder Funktion deklarieren, können Sie eine Aufrufkonvention mit einer der Direktiven register, pascal, cdecl, stdcall, safecall und winapi angeben. Zum Beispiel:

 function MyFunction(X, Y: Real): Real; cdecl;

Aufrufkonventionen bestimmen die Reihenfolge, in der Parameter an die Routine übergeben werden. Sie beeinflussen außerdem das Entfernen der Parameter vom Stack, den Einsatz der Register zur Übergabe von Parametern sowie die Fehler- und Exception-Behandlung. Die Standard-Aufrufkonvention ist register.

  • Für die Konventionen register und pascal ist die Auswertungsreihenfolge nicht definiert.
  • Die Konventionen cdecl, stdcall und safecall übergeben die Parameter von rechts nach links.
  • Bei allen Konventionen mit Ausnahme von cdecl entfernt die Prozedur bzw. Funktion die Parameter vom Stack, sobald die Steuerung zurückgegeben wird. Bei der Konvention cdecl entfernt die aufrufende Routine die Parameter vom Stack, sobald sie wieder die Steuerung erhält.
  • Die Konvention register verwendet bis zu drei CPU-Register zur Übergabe der Parameter, alle anderen Konventionen übergeben die Parameter an den Stack.
  • Die Konvention safecall implementiert "Exception-Firewalls" und in Win32 die prozessübergreifende COM-Fehlerbenachrichtigung.
  • winapi ist eigentlich keine Aufrufkonvention. winapi legt fest, dass die standardmäßige Aufrufkonvention der Plattform verwendet wird. Unter Win32 entspricht winapi der Aufrufkonvention stdcall.

Die folgende Tabelle beschreibt die Aufrufkonventionen.

Aufrufkonventionen

Direktive   Parameterreihenfolge   Bereinigung   Parameterübergabe in Registern?

register 

Undefiniert 

Routine 

Ja

pascal

Undefiniert

Routine

Nein

cdecl

Von rechts nach links

Aufrufender

Nein

stdcall

Von rechts nach links

Routine

Nein

safecall

Von rechts nach links

Routine

Nein


Die weitaus effizienteste Aufrufkonvention ist register, da hier meistens kein Stack-Frame für die Parameter angelegt werden muss. (Zugriffsmethoden für Eigenschaften, die als published deklariert sind, müssen register verwenden.) Die Konvention cdecl ist hilfreich, wenn Funktionen in gemeinsam benutzten Bibliotheken aufgerufen werden, die in C oder C++ geschrieben wurden. Die Konventionen stdcall und safecall sollten für Aufrufe von externem Quelltext benutzt werden. In Win32 verwenden die Betriebssystem-APIs die Konventionen stdcall und safecall. Andere Betriebssysteme verwenden normalerweise die Konvention cdecl (beachten Sie, dass stdcall effizienter ist als cdecl).

Die Konvention safecall muss bei der Deklaration von Methoden für duale Interfaces verwendet werden. Die Konvention pascal gewährleistet die Abwärtskompatibilität.

Die Direktiven near, far und export beziehen sich auf die Aufrufkonventionen bei der Programmierung für 16-Bit-Windows-Umgebungen. Sie dienen ausschließlich der Abwärtskompatibilität und haben in Win32-Anwendungen keine Auswirkung.

forward- und interface-Deklarationen

Die Direktive forward in einer Prozedur- oder Funktionsdeklaration wird durch einen Rumpf mit Anweisungen und lokalen Variablendeklarationen ersetzt. Zum Beispiel:

 function Calculate(X, Y: Integer): Real; forward;

Die hier deklarierte Funktion Calculate enthält die Direktive forward. An einer anderen Position muss der Rumpf der Routine deklariert werden. Diese definierende Deklaration für Calculate kann beispielsweise folgendermaßen aussehen:

 function Calculate;
   ... { declarations }
 begin
   ... { statement block }
 end;

Grundsätzlich muss eine definierende Deklaration die Parameterliste oder den Rückgabetyp der Routine nicht wiederholen. Werden diese Angaben jedoch wiederholt, müssen sie denen in der forward-Deklaration exakt entsprechen. Standardparameter in der definierenden Deklaration können aber weggelassen werden. Wenn die forward-Deklaration eine überladene Prozedur oder Funktion angibt, muss die Parameterliste in der definierenden Deklaration wiederholt werden.

Eine forward-Deklaration und die dazugehörige definierende Deklaration müssen in demselben type-Deklarationsabschnitt enthalten sein. Sie können also keinen neuen Abschnitt (z. B. einen var- oder const-Abschnitt) zwischen der forward-Deklaration und der definierenden Deklaration einfügen. Die definierende Deklaration kann eine external- oder assembler-Deklaration sein, es darf sich jedoch nicht um eine weitere forward-Deklaration handeln.

Mit einer forward-Deklaration kann der Gültigkeitsbereich eines Prozedur- oder Funktionsbezeichners auf einen früheren Punkt im Quelltext ausgedehnt werden. Andere Prozeduren und Funktionen können also die mit forward deklarierte Routine aufrufen, bevor sie tatsächlich definiert wurde. forward-Deklarationen bieten nicht nur mehr Flexibilität bei der Programmierung, sie werden auch gelegentlich für Rekursionen benötigt.

Die Direktive forward hat keine Auswirkung im interface-Abschnitt einer Unit. Prozedur- und Funktions-Header im interface-Abschnitt verhalten sich wie forward-Deklarationen. Die zugehörigen definierenden Deklarationen müssen in den implementation-Abschnitt eingefügt werden. Eine im interface-Abschnitt deklarierte Routine ist von jeder Position in der Unit und für jede Unit und jedes Programm verfügbar, die bzw. das die Unit mit der Deklaration einbindet.

external-Deklarationen

Die Direktive external ersetzt den Anweisungsblock in einer Prozedur- oder Funktionsdeklaration. Sie ermöglicht das Aufrufen von Routinen, die unabhängig vom aktuellen Programm compiliert wurden. Externe Routinen stammen aus Objektdateien oder aus dynamisch ladbaren Bibliotheken.

Verwenden Sie zum Importieren einer C-Funktion, die eine variable Anzahl von Parametern übernimmt, die Direktive varargs. Zum Beispiel:

 function printf(Format: PChar): Integer; cdecl; varargs;

Die Direktive varargs kann nur bei externen Routinen und zusammen mit der Aufrufkonvention cdecl eingesetzt werden.

Objektdateien linken

Um Routinen aus einer separat compilierten Objektdatei aufrufen zu können, müssen Sie die Objektdatei mit der Compiler-Direktive $L (oder $LINK) zur gewünschten Anwendung linken. Zum Beispiel:

 {$L BLOCK.OBJ}

Diese Anweisung linkt die Datei BLOCK.OBJ zu dem Programm bzw. der Unit, in der die Anweisung verwendet wird. Anschließend müssen Sie die aufzurufenden Funktionen und Prozeduren deklarieren:

 procedure MoveWord(var Source, Dest; Count: Integer); external;
 procedure FillWord(var Dest; Data: Integer; Count: Integer); external;

Jetzt können Sie die Routinen MoveWord und FillWord in BLOCK.OBJ aufrufen.

In Win32 werden Deklarationen wie die oben gezeigten häufig verwendet, um auf externe Assembler-Routinen zuzugreifen. Assembler-Routinen können auch direkt in Delphi-Quelltext eingefügt werden.

Funktionen aus Bibliotheken importieren

Mit einer Direktive der Form

external StringKonstante;

können Sie Routinen aus einer dynamisch ladbaren Bibliothek (.DLL-Datei) importieren. Die Anweisung muss an das Ende des Prozedur- bzw. Funktions-Headers angefügt werden. stringConstant gibt die Bibliotheksdatei in einfachen Anführungszeichen (Hochkommas) an. Ein Beispiel:

function SomeFunction(S: string): string; external 'strlib.dll';

Mit dieser Anweisung wird in Win32 die Funktion SomeFunction aus der Datei strlib.dll importiert.

Interne und externe Linker verwenden

Der Delphi-Compiler interpretiert "External" unterschiedlich, je nachdem, ob der Compiler einen externen Linker verwendet nicht.


1. Von Delphi unterstützte Plattformen können in die zwei folgenden Gruppen unterteilt werden:

  • Der Compiler verwendet den eigenen internen Linker.
  • Der Compiler verwendet einen externen Linker.

Verwenden des internen Linkers 

WIN32, WIN64, OSX, IOS-Simulator 

Verwenden eines externen Linkers 

iOS-Device, Android, Linux, macOS64 

2. Auf Plattformen, auf denen Delphi einen internen Linker verwendet, gibt external <stringconstant> an, dass sich die Funktion/Prozedur in einer DLL, dylib oder einem gemeinsamen Objekt (Shared Object) befindet.

Auf diesen Plattformen geht Delphi davon aus, dass das Symbol aus einer .dll/.dylib/.so. importiert wird. Beim Linken wird keine Validierung vorgenommen. Delphi generiert ein Image mit einer Referenz auf das Symbol/die Bibliothek. Falls sich das Symbol nicht in dieser Bibliothek befindet, wird das bei der Ausführung deutlich.

3. Auf Plattformen, auf denen Delphi einen externen Linker verwendet, beispielsweise wenn die iOSDevice32-Plattform das Ziel ist, wird der in external <stringconstant> angegebene Bezeichner an den externen Linker übergeben.

Der Delphi-Compiler übergibt <name> an ld.exe. Wenn der Compiler die Bibliothek nicht findet, zeigt er die folgende Fehlermeldung an: Fehler: E2597 ld: Datei nicht gefunden: <name>.

Funktionen aus Objektdateien importieren (nur externer Linker)

Wenn ein externer Linker verwendet wird, können Sie die Verwendung der Compiler-Direktive $L (oder $LINK) unterdrücken, indem Sie die Objektdatei mit der external-Direktive angeben. Zum Beispiel:

procedure FunctionName; cdecl; external object 'ObjectFile.o' name '_FunctionName';

Funktionen aus Frameworks importieren

Sie können keine Routinen aus einem externen Framework importieren. Zum Beispiel:

Function foo: Integer; external framework 'framework name>'

Dies gilt nur für iOS32, iOS64 und macOS64.

Eine Routine unter einem anderen Namen importieren

Sie können Routinen unter einem anderen als dem in der Bibliothek verwendeten Namen importieren, indem Sie den Originalnamen in der external-Direktive angeben:

external stringKonstante1 name StringKonstante2;

Die erste StringKonstante gibt den Namen der Bibliotheksdatei, die zweite den Originalnamen der Routine an.

Die folgende Deklaration importiert eine Funktion aus user32.dll (Bestandteil der Windows-API):

function MessageBox(HWnd: Integer; Text, Caption: PChar; Flags: Integer): Integer; stdcall; external 'user32.dll' name 'MessageBoxA';

Der Originalname der Funktion lautet MessageBoxA, sie wird jedoch unter dem Namen MessageBox importiert.

Stellen Sie in Ihrer Import-Deklaration sicher, dass die Schreibweise und der Fall des Routinenamens übereinstimmen. Beim Aufruf der importierten Routine wird die Groß-/Kleinschreibung nicht mehr berücksichtigt.

Eine Routine über den Index importieren

Sie können die zu importierende Routine auch über eine Nummer angeben:

external StringKonstante Index IntegerKonstante;

integerConstant ist der Index der Routine in der Exporttabelle.

Laden der Bibliothek verzögern

Um das Laden der Bibliothek, die die Funktion enthält, bis zu dem Moment zu verzögern, an dem die Funktion wirklich benötigt wird, hängen Sie die Direktive delayed an die importierte Funktion an:

function ExternalMethod(const SomeString: PChar): Integer; stdcall; external 'cstyle.dll' delayed;

delayed stellt sicher, dass die Bibliothek, die die importierte Funktion enthält, nicht beim Start der Anwendung geladen wird, sondern erst, wenn die Funktion aufgerufen wird. Weitere Informationen zu diesem Thema finden Sie unter Bibliotheken und Packages - Verzögertes Laden.

Abhängigkeit von der Bibliothek festlegen

Wenn die Bibliothek, die die Zielroutine beinhaltet, von anderen Bibliotheken abhängt, verwenden Sie die dependency-Direktive, um diese Abhängigkeit festzulegen.

Um die dependency-Direktive zu verwenden, fügen Sie das Schlüsselwort dependency, gefolgt von einer durch ein Komma getrennten String-Liste an. Jeder String muss einen Namen einer Bibliothek enthalten, die von einer externen Zielbibliothek abhängig ist:

external <library> dependency <dependency1>, <dependency2>, …

Die folgende Deklaration gibt an, dass libmidas.a von der Standard-C++-Bibliothek abhängig ist:

function DllGetDataSnapClassObject(const [REF] CLSID, [REF] IID: TGUID; var Obj): HResult; cdecl; external 'libmidas.a' dependency 'stdc++';

Prozeduren und Funktionen überladen

Sie können mehrere Routinen mit identischen Namen in demselben Gültigkeitsbereich deklarieren. Dieses Verfahren wird Überladen genannt. Überladene Routinen müssen mit der Direktive overload deklariert werden und unterschiedliche Parameterlisten haben. Beispiele:

 function Divide(X, Y: Real): Real; overload;
 begin
   Result := X/Y;
 end

 function Divide(X, Y: Integer): Integer; overload;
 begin
   Result := X div Y;
 end;

Diese Deklarationen definieren zwei Funktionen namens Divide, die Parameter mit unterschiedlichen Typen entgegennehmen. Wenn Sie Divide aufrufen, ermittelt der Compiler die zu verwendende Funktion durch Prüfung des übergebenen Parametertyps. Divide(6.0, 3.0) ruft beispielsweise die erste Divide-Funktion auf, weil es sich bei den Argumenten um reelle Zahlen handelt.

Einer überladenen Routine können Parameter übergeben werden, deren Typ keinem der in den Routinendeklarationen verwendeten Typen entspricht. Voraussetzung dafür ist, dass diese Parameter zuweisungskompatibel mit den Parametern in mehr als einer Deklaration sind. Dieses Vorgehen wird meist beim Überladen von Routinen mit unterschiedlichen Integer- oder Real-Typen verwendet:

 procedure Store(X: Longint); overload;
 procedure Store(X: Shortint); overload;

Wenn die Eindeutigkeit gewährleistet ist, ruft der Compiler in diesen Fällen die Routine auf, deren Parametertyp für den Wertebereich der tatsächlich übergebenen Parameter mindestens erforderlich ist. (Beachten Sie, dass konstante Ausdrücke, die reelle Zahlen sind, immer vom Typ Extended sind.)

Überladene Routinen müssen hinsichtlich der Anzahl der entgegengenommenen Parameter oder der Typen dieser Parameter eindeutig sein. Die folgenden beiden Deklarationen führen deshalb zu einem Fehler bei der Compilierung:

 function Cap(S: string): string; overload;
   ...
 procedure Cap(var Str: string); overload;
   ...

Dagegen sind die Deklarationen

 function Func(X: Real; Y: Integer): Real; overload;
   ...
 function Func(X: Integer; Y: Real): Real; overload;
   ...

zulässig.

Wird eine überladene Routine mit forward oder "interface" deklariert, muss die Parameterliste in der definierenden Deklaration der Routine wiederholt werden.

Der Compiler kann zwischen überladenen Funktionen unterscheiden, die AnsiString/PAnsiChar, UnicodeString/PChar und WideString/PWideChar Parameter in derselben Parameterposition enthalten. String-Konstanten oder String-Literale, die in eine solche überladene Position übergeben werden, werden in den nativen String- oder Zeichentyp übersetzt, der UnicodeString/PChar lautet.

 procedure test(const A: AnsiString); overload;
 procedure test(const W: WideString); overload;
 procedure test(const U: UnicodeString); overload;
 procedure test(const PW: PWideChar); overload;
 var
   a: AnsiString;
   b: WideString;
   c: UnicodeString;
   d: PWideChar;
   e: string;
 begin
   a := 'a';
   b := 'b';
   c := 'c';
   d := 'd';
   e := 'e';
   test(a);    // calls AnsiString version
   test(b);    // calls WideString version
   test(c);    // calls UnicodeString version
   test(d);    // calls PWideChar version
   test(e);    // calls UnicodeString version
   test('abc');    // calls UnicodeString version
   test(AnsiString ('abc'));    // calls AnsiString version
   test(WideString('abc'));    // calls WideString version
   test(PWideChar('PWideChar'));    // calls PWideChar version
 end;

Varianten können ebenfalls als Parameter in überladenen Funktionsdeklarationen verwendet werden. Eine Variante wird allgemeiner angesehen als ein beliebiger einfacher Typ. Exakte Typübereinstimmungen werden stets gegenüber Variantenübereinstimmungen bevorzugt. Wird eine Variante an eine solche überladene Position übergeben, und ist eine overload-Anweisung an dieser Parameterposition vorhanden, die eine Variante akzeptiert, wird diese als exakte Übereinstimmung für den Variant-Typ angesehen.

Dies kann bei Gleitkommatypen einige geringe Nebeneffekte bewirken. Gleitkommatypen werden anhand der Größe verglichen. Falls es keine genaue Übereinstimmung für die Gleitkommavariable gibt, die an den overload-Aufruf übergeben wird, aber ein varianter Parameter vorhanden ist, erhält die Variante den Vorzug gegenüber dem kleineren Gleitkommatyp.

Zum Beispiel:

 procedure foo(i: integer); overload;
 procedure foo(d: double); overload;
 procedure foo(v: variant); overload;
 var
   v: variant;
 begin
   foo(1);       // integer version
   foo(v);       // variant version
   foo(1.2);     // variant version (float literals -> extended precision)
 end;

Dieses Beispiel ruft die variant-Version von foo, nicht die double-Version auf, da die Konstante 1.2 implizit ein extended-Typ ist. extended ist jedoch keine genaue Übereinstimmung für double. Extended ist auch keine Übereinstimmung für Variant, aber Variant wird als allgemeinerer Typ angesehen (während Double ein kleinerer Typ als Extended ist).

 foo(Double(1.2));

Diese Typumwandlung funktioniert nicht. Verwenden Sie stattdessen typisierte Konstanten:

 const  d: double = 1.2;
   begin
     foo(d);
   end;

Dieser Quelltext arbeitet korrekt und ruft die double-Version auf.

 const  s: single = 1.2;
   begin
     foo(s);
   end;

Dieser Quelltext funktioniert und ruft die double-Version von foo auf. Single passt besser zu double als variant.

Bei der Deklaration von überladenen Routinen besteht die beste Möglichkeit zur Vermeidung einer Gleitkommapromotion zu Variant darin, eine Version ihrer überladenen Funktion für jeden Gleitkommatyp (Single, Double, Extended) zusammen mit der Variant-Version zu deklarieren.

Bei Verwendung von Standardparametern in überladenen Routinen müssen Sie mehrdeutige Parametersignaturen vermeiden.

Die möglichen Auswirkungen des Überladens lassen sich beschränken, indem Sie beim Aufruf den qualifizierten Routinennamen verwenden. Mit Unit1.MyProcedure(X, Y) können nur die in Unit1; deklarierten Routinen aufgerufen werden. Wenn der Name und die Parameterliste keiner Routine in Unit1 entsprechen, gibt der Compiler einen Fehler aus.

Lokale Deklarationen

Der Rumpf einer Funktion oder Prozedur beginnt in vielen Fällen mit der Deklaration lokaler Variablen, die im Anweisungsblock der Routine verwendet werden. Sie können beispielsweise Konstanten, Typen und andere Routinen deklarieren. Der Gültigkeitsbereich lokaler Bezeichner ist auf die Routine beschränkt, in der sich die Deklaration befindet.

Verschachtelte Routinen

Funktionen und Prozeduren können im Abschnitt mit den lokalen Deklarationen ihrerseits Funktionen und Prozeduren enthalten. Die folgende Deklaration der Prozedur DoSomething enthält beispielsweise eine verschachtelte Prozedur:

 procedure DoSomething(S: string);
 var
   X, Y: Integer;

   procedure NestedProc(S: string);
   begin
   ...
   end;

 begin
   ...
   NestedProc(S);
   ...
 end;

Der Gültigkeitsbereich einer verschachtelten Routine ist auf die Funktion bzw. Prozedur beschränkt, in der sie deklariert ist. Im letzten Beispiel kann NestedProc nur in DoSomething aufgerufen werden..

Echte Beispiele verschachtelter Routinen sind die Prozedur DateTimeToString, die FunktionScanDate und andere Routinen in der Unit SysUtils.

Siehe auch