Parameter (Delphi)

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Prozeduren und Funktionen - Index


Dieses Thema enthält Informationen zu folgenden Bereichen:

  • Parametersemantik
  • String-Parameter
  • Array-Parameter
  • Standardparameter

Allgemeines zu Parametern

Die meisten Prozedur- und Funktions-Header enthalten eine Parameterliste. Im Header:

function Power(X: Real; Y: Integer): Real;

lautet die Parameterliste beispielsweise (X: Real; Y: Integer).

Eine Parameterliste ist eine Folge von Parameterdeklarationen, die durch Semikolon voneinander getrennt und in Klammern eingeschlossen werden. Jede Deklaration besteht aus einer Reihe von Parameternamen, die durch Komma voneinander getrennt werden. Hinter den Parameternamen steht in den meisten Fällen ein Doppelpunkt und ein Typbezeichner, in Einzelfällen außerdem das Gleichheitszeichen (=) und ein Standardwert. Parameternamen müssen gültige Bezeichner sein. Jeder Deklaration kann var, const oder out vorausgehen. Beispiele:

(X, Y: Real)
(var S: string; X: Integer)
(HWnd: Integer; Text, Caption: PChar; Flags: Integer)
(const P; I: Integer)

Die Parameterliste gibt Anzahl, Reihenfolge und Typ der Parameter an, die beim Aufruf an die Routine übergeben werden müssen. Wenn eine Routine keine Parameter entgegennimmt, geben Sie in der Deklaration weder die Bezeichnerliste noch die Klammern an:

procedure UpdateRecords;
begin
  ...
end;

Im Rumpf der Prozedur oder Funktion können die Parameternamen (X und Y im ersten Beispiel) als lokale Variablen eingesetzt werden. Sie dürfen die Parameternamen im Abschnitt mit den lokalen Deklarationen nicht erneut deklarieren.

Parametersemantik

Parameter können auf verschiedene Weise kategorisiert werden:

  • Jeder Parameter ist als Wert-, Variablen-, Konstanten- oder Ausgabeparameter klassifiziert. Wertparameter werden verwendet, sofern nicht mit den reservierten Wörtern var, const und out einer der anderen Typen angegeben wird.
  • Wertparameter sind immer typisiert, während Variablen-, Konstanten und Ausgabeparameter auch untypisiert sein können.
  • Für Array-Parameter gelten spezielle Regeln.

Dateien und Instanzen strukturierter Typen, die Dateien enthalten, können nur als Variablenparameter (var) übergeben werden.

Wert- und Variablenparameter

Die meisten Parameter sind Wert- (Standard) oder Variablenparameter (var). Wertparameter werden als Wert übergeben, Variablenparameter als Referenz. Der Unterschied wird anhand der folgenden beiden Funktionen verdeutlicht:

function DoubleByValue(X: Integer): Integer;   // X is a value parameter
begin
  X := X * 2;
  Result := X;
end;
 
function DoubleByRef(var X: Integer): Integer;  // X is a variable parameter
begin
  X := X * 2;
  Result := X;
end;

Diese Funktionen liefern dasselbe Ergebnis. Nur die zweite Funktion (DoubleByRef) kann jedoch den Wert der an sie übergebenen Variable ändern. Die Funktionen können beispielsweise folgendermaßen aufgerufen werden:

var
  I, J, V, W: Integer;
begin
  I := 4;
  V := 4;
  J := DoubleByValue(I);   // J = 8, I = 4
  W := DoubleByRef(V);     // W = 8, V = 8
end;

Nachdem diese Anweisungen ausgeführt wurden, enthält die an DoubleByValue übergebene Variable I immer noch den ursprünglichen Wert. Die an DoubleByRef übergebene Variable V enthält dagegen einen neuen Wert.

Ein Wertparameter verhält sich wie eine lokale Variable, die durch den im Aufruf der Funktion oder Prozedur übergebenen Wert initialisiert wird. Wenn Sie eine Variable als Wertparameter übergeben, erstellt die Prozedur oder Funktion eine Kopie dieser Variable. Änderungen des Wertes dieser Variable wirken sich nicht auf die ursprüngliche Variable aus. Sie werden verworfen, sobald die Steuerung wieder an den Aufrufer zurückgegeben wird.

Ein Variablenparameter ist dagegen mit einem Zeiger vergleichbar. Änderungen des Parameters im Rumpf einer Prozedur oder Funktion bleiben deshalb erhalten, wenn die Programmausführung wieder an den Aufrufer übergeben wird, und der Parametername nicht mehr im Gültigkeitsbereich liegt.

Auch wenn dieselbe Variable in mehreren var-Parametern übergeben wird, werden keine Kopien erstellt. Dieses Verhalten wird im folgenden Beispiel illustriert:

procedure AddOne(var X, Y: Integer);
begin
  X := X + 1;
  Y := Y + 1;
end;

var I: Integer;
begin
  I := 1;
  AddOne(I, I);
end;

Nachdem dieser Code ausgeführt wurde, enthält die Variable I den Wert 3.

Enthält die Deklaration einer Routine einen var-Parameter, müssen Sie im Aufruf der Routine einen Ausdruck übergeben, dem ein Wert zugewiesen werden kann, also eine Variable, eine typisierte Konstante (im Status {$J+}), einen dereferenzierten Zeiger, ein Feld oder eine indizierte Variable. Im Beispiel weiter oben würde DoubleByRef(7) einen Fehler erzeugen, während der Aufruf DoubleByValue(7) zulässig wäre.

In var-Parametern (beispielsweise DoubleByRef(MyArray[I])) übergebene Indizes und dereferenzierte Zeiger werden vor der Ausführung der Routine einmal ausgewertet.

Konstantenparameter

Ein Konstantenparameter (const) entspricht einer lokalen bzw. schreibgeschützten Variable. Konstantenparameter entsprechen weitgehend Wertparametern. Sie können ihnen jedoch im Rumpf einer Prozedur oder Funktion keinen Wert zuweisen und sie nicht als var-Parameter an eine andere Routine übergeben. Wenn Sie aber eine Objektreferenz als Konstantenparameter übergeben, können Sie weiterhin auf die Objekteigenschaften zugreifen und diese ändern.

Die Verwendung von const ermöglicht dem Compiler die Optimierung des Codes für strukturierte und String-Parameter. Gleichzeitig wird die versehentliche Übergabe eines Parameters als Referenz an eine andere Routine verhindert.

Das folgende Beispiel ist der Header der Funktion CompareStr in der Unit SysUtils:

function CompareStr(const S1, S2: string): Integer;

Da S1 und S2 im Rumpf von CompareStr nicht geändert werden, können sie als Konstantenparameter deklariert werden.

Konstantenparameter können je nach verwendetem Compiler an die Funktion per Wert oder per Referenz übergeben werden. Um den Compiler zu veranlassen, einen Konstantenparameter per Referenz zu übergeben, können Sie den Dekorator [Ref] mit dem Schlüsselwort const verwenden.

Das folgende Beispiel zeigt, wie Sie den Dekorator [Ref] entweder vor oder nach dem Schlüsselwort const angeben können:

function FunctionName(const [Ref] parameter1: Class1Name; [Ref] const parameter2: Class2Name);

Ausgabeparameter

Ausgabeparameter werden mit dem Schlüsselwort out deklariert und wie Variablenparameter per Referenz übergeben. Der ursprüngliche Wert der referenzierten Variable wird jedoch verworfen, wenn diese als Ausgabeparameter an eine Routine übergeben wird. Der Ausgabeparameter dient nur der Ausgabe. Er weist die Funktion oder Prozedur an, wo die Ausgabe zu speichern ist, stellt aber keinerlei Eingaben bereit.

Ein Beispiel ist der folgende Prozedur-Header:

procedure GetInfo(out Info: SomeRecordType);

Wenn Sie GetInfo aufrufen, müssen Sie eine Variable des Typs SomeRecordType übergeben:

var MyRecord: SomeRecordType;
   ...
GetInfo(MyRecord);

In MyRecord werden jedoch keine Daten an die Prozedur GetInfo übergeben; MyRecord dient nur als Container für die von GetInfo generierten Informationen. Beim Aufruf von GetInfo wird der von MyRecord belegte Speicher sofort freigegeben, noch bevor die Steuerung an die Prozedur übergeben wird.

Ausgabeparameter werden häufig in verteilten Objektmodellen wie COM eingesetzt. Sie sollten diesen Parametertyp auch verwenden, wenn Sie nicht initialisierte Variablen an Funktionen oder Prozeduren übergeben.

Untypisierte Parameter

Variablen-, Konstanten- und Ausgabeparameter (var, const und out) müssen im Gegensatz zu Wertparametern nicht typisiert werden. Beispiel:

procedure TakeAnything(const C);

Diese Anweisung deklariert eine Prozedur namens TakeAnything, die einen Parameter beliebigen Typs entgegennimmt. Wenn Sie eine derartige Routine aufrufen, können Sie keine Zahl oder untypisierte numerische Konstante an sie übergeben.

Im Rumpf einer Prozedur oder Funktion sind untypisierte Parameter mit keinem Typ kompatibel. Bevor der untypisierte Parameter bearbeitet werden kann, muss eine Typkonvertierung erfolgen. Der Compiler kann nicht feststellen, ob Operationen, die mit untypisierten Parametern durchgeführt werden, gültig sind.

Die folgende Beispielfunktion Equal vergleicht die angegebene Anzahl Byte in zwei beliebigen Variablen, die als untypisierte Parameter übergeben werden:

function Equal(var Source, Dest; Size: Integer): Boolean;
type
  TBytes = array[0..MaxInt - 1] of Byte;
var
  N : Integer;
begin
  N := 0;
  while (N < Size) and (TBytes(Dest)[N] = TBytes(Source)[N]) do
    Inc(N);
  Equal := N = Size;
end;

Nach den Deklarationen:

type
  TVector = array[1..10] of Integer;
  TPoint = record
    X, Y: Integer;  // Integer occupies 4 bytes. Therefore 8 bytes in a whole
  end;
var
  Vec1, Vec2: TVector;
  N: Integer;
  P: TPoint;

kann die Funktion Equal folgendermaßen aufgerufen werden:

Equal(Vec1, Vec2, SizeOf(TVector));      // compare Vec1 to Vec2
Equal(Vec1, Vec2, SizeOf(Integer) * N);  // compare first N 
                                         // elements of Vec1 and Vec2
Equal(Vec1[1], Vec1[6], SizeOf(Integer) * 5); // compare first 5 to
                                              // last 5 elements of Vec1
Equal(Vec1[1], P, 8);                    // compare Vec1[1] to P.X and Vec1[2] to P.Y
                                         // each Vec1[x] is integer and occupies 4 bytes

String-Parameter

Bei der Deklaration von Routinen, die kurze Strings als Parameter entgegennehmen, können Sie in den Parameterdeklarationen keine Längenangaben verwenden. Die folgende Deklaration führt zu einem Fehler bei der Compilierung:

procedure Check(S: string[20]);   // syntax error

Aber die folgende Deklaration ist gültig:

type TString20 = string[20];
procedure Check(S: TString20);

Mit dem speziellen Bezeichner OpenString können Sie Routinen deklarieren, die kurze Strings unterschiedlicher Länge als Parameter entgegennehmen:

procedure Check(S: OpenString);

Wenn die Compiler-Direktiven {$H} und {$P+} aktiviert sind, ist das reservierte Wort string in Parameterdeklarationen gleichbedeutend mit OpenString.

Kurze Strings, OpenString, $H und $P werden nur aus Gründen der Abwärtskompatibilität unterstützt. In neuem Quelltext sollten Sie immer lange Strings verwenden.

Array-Parameter

Bei der Deklaration von Routinen, die Array-Parameter entgegennehmen, können Sie in den Parameterdeklarationen keine Indextypbezeichner angeben. Beispiel:

procedure Sort(A: array[1..10] of Integer)  // syntax error

Diese Deklaration führt zu einem Compilierungsfehler. Aber:

type TDigits = array[1..10] of Integer;
procedure Sort(A: TDigits);

ist zulässig. Eine andere Möglichkeit ist die Verwendung von offenen Array-Parametern.

Da in Delphi keine Wertesemantik für dynamische Arrays implementiert ist, stellen "Wert"-Parameter in Routinen keine vollständige Kopie des dynamischen Arrays dar. Beispiel:

type
  TDynamicArray = array of Integer;
  procedure p(Value: TDynamicArray);
    begin
      Value[0] := 1;
    end;

  procedure Run;
    var
      a: TDynamicArray;
    begin
      SetLength(a, 1);
      a[0] := 0;
      p(a);
      Writeln(a[0]); // Prints '1'
    end;

Beachten Sie, dass die Zuweisung zu Value[0] in der Routine p den Inhalt des dynamischen Arrays der aufrufenden Routine ändert, obwohl Value ein Wertparameter ist. Falls eine vollständige Kopie des dynamischen Arrays erforderlich ist, verwenden Sie die Standardprozedur Copy, um eine Wertkopie des dynamischen Arrays zu erstellen.

Offene Array-Parameter

Offene Array-Parameter ermöglichen die Übergabe von Arrays unterschiedlicher Größe an dieselbe Funktion oder Prozedur. Die Definition eines offenen Array-Parameters erfolgt mit der Syntax array of Typ (anstelle der Syntax array[X..Y] of Typ) in der Parameterdeklaration. Beispiel:

function Find(A: array of Char): Integer;

Hier wird eine Funktion namens Find deklariert, die ein Zeichen-Array beliebiger Größe entgegennimmt und einen Integer-Wert zurückgibt.

Hinweis: Die Syntax offener Array-Parameter erinnert an dynamische Arrays, obwohl diese beiden Array-Typen nicht identisch sind. Das obige Beispiel deklariert eine Funktion, die ein beliebiges Zeichen-Array entgegennimmt, also auch ein dynamisches Array. Die Deklaration eines Parameters, bei dem es sich um ein dynamisches Array handeln soll, muss mit Angabe eines Typbezeichners erfolgen:

type TDynamicCharArray = array of Char;
function Find(A: TDynamicCharArray): Integer;

Im Rumpf einer Routine gelten die folgenden Regeln für offene Array-Parameter:

  • Sie sind stets nullbasiert. Das erste Element trägt immer die Indexnummer 0, das zweite die 1 usw. Die Standardfunktionen Low und High geben 0 bzw. Length - 1 zurück. Die Funktion SizeOf gibt die Größe des Arrays zurück, das an die Routine übergeben wird.
  • Der Zugriff kann nur auf einzelne Elemente erfolgen. Zuweisungen an einen offenen Array-Parameter insgesamt sind nicht zulässig.
  • Sie können nur als offene Array-Parameter oder als untypisierte Variablenparameter (var) an andere Prozeduren und Funktionen übergeben werden. Eine Übergabe an SetLength ist nicht möglich.
  • Anstelle eines Arrays können Sie eine Variable mit dem Basistyp des offenen Array-Parameters übergeben, die dann wie ein Array der Länge 1 behandelt wird.

Wenn Sie ein Array als offenen Array-Wertparameter übergeben, erstellt der Compiler im Stack-Bereich der Routine eine lokale Kopie des Arrays. Die Übergabe großer Parameter kann also zu einem Stack-Überlauf führen.

Die folgenden Beispiele verwenden offene Array-Parameter, um eine Prozedur namens Clear zu definieren, die jedem Element eines Arrays mit reellen Zahlen den Wert null zuweist. Außerdem wird die Funktion Sum definiert, mit der die Summe der Elemente in einem Array mit reellen Zahlen ermittelt werden kann:

procedure Clear(var A: array of Real);
var
   I: Integer;
begin
   for I := 0 to High(A) do A[I] := 0;
end;

function Sum(const A: array of Real): Real;
var
  I: Integer;
  S: Real;
begin
  S := 0;
  for I := 0 to High(A) do S := S + A[I];
  Sum := S;
end;

Wenn Sie Routinen aufrufen, die offene Array-Parameter verarbeiten, können Sie offene Array-Konstruktoren übergeben.

Variante offene Array-Parameter

Variante offene Array-Parameter ermöglicht die Übergabe eines Arrays mit Ausdrücken unterschiedlicher Typen an eine Prozedur oder Funktion. In der Definition einer Routine mit einem varianten offenen Array-Parameter geben Sie als Typ des Parameters array of const an. Beispiel:

procedure DoSomething(A: array of const);

Hier wird eine Prozedur namens DoSomething deklariert, die auch heterogene Arrays verarbeiten kann.

Die Konstruktion array of const ist zur Konstruktion array of TVarRec äquivalent. System.TVarRec, der in der Unit System deklariert ist, repräsentiert einen Record mit variantem Bestandteil, der Werte der Typen Integer, Boolean, Zeichen, Real, String, Zeiger, Klasse, Klassenreferenz, Interface und Variant aufnehmen kann. Das Feld VType im Record TVarRec gibt den Typ der einzelnen Elemente im Array an. Einige Typen werden nicht per Wert, sondern per Referenz übergeben; Strings werden als Pointer übergeben und müssen in den Typ string umgewandelt werden.

Im folgenden Win32-Beispiel wird ein varianter offener Array-Parameter in einer Funktion verwendet, die aus jedem Element im Array einen String erzeugt. Die einzelnen Strings werden dann verkettet. Die in dieser Funktion aufgerufenen Routinen zur String-Verarbeitung sind in SysUtils definiert:

function MakeStr(const Args: array of const): string;
var
  I: Integer;
begin
  Result := '';
  for I := 0 to High(Args) do
     with Args[I] do
        case VType of
          vtInteger:  Result := Result + IntToStr(VInteger);
          vtBoolean:  Result := Result + BoolToStr(VBoolean);
          vtChar:     Result := Result + VChar;
          vtExtended: Result := Result + FloatToStr(VExtended^);
          vtString:   Result := Result + VString^;
          vtPChar:    Result := Result + VPChar;
          vtObject:   Result := Result + VObject.ClassName;
          vtClass:    Result := Result + VClass.ClassName;
          vtAnsiString:  Result := Result + string(VAnsiString);
          vtUnicodeString:  Result := Result + string(VUnicodeString);
          vtCurrency:    Result := Result + CurrToStr(VCurrency^);
          vtVariant:     Result := Result + string(VVariant^);
          vtInt64:       Result := Result + IntToStr(VInt64^);
  end;
end;

Der Aufruf dieser Funktion kann mit einem offenen Array-Konstruktor erfolgen. Beispiel:

MakeStr(['test', 100, ' ', True, 3.14159, TForm])

Dieser Aufruf gibt den String 'test100 T3.14159TForm' zurück.

Standardparameter

Sie können im Header einer Prozedur oder Funktion Standardparameterwerte angeben. Standardwerte sind nur für typisierte Konstanten- und für Wertparameter zulässig. Die Angabe des Standardwertes erfolgt mit dem Gleichheitszeichen (=) hinter der Parameterdeklaration und einem Konstantenausdruck, der zum Typ des Parameters zuweisungskompatibel ist.

Wenn eine Funktion oder Prozedur beispielsweise die Deklaration:

procedure FillArray(A: array of Integer; Value: Integer = 0);

hat, sind die folgenden Prozeduraufrufe äquivalent:

FillArray(MyArray);
FillArray(MyArray, 0);

In einer Deklaration mehrerer Parameter kann kein Standardwert angegeben werden. Die folgende Deklaration ist zulässig:

function MyFunction(X: Real = 3.5; Y: Real = 3.5): Real;

Die folgende Deklaration ist dagegen nicht zulässig:

function MyFunction(X, Y: Real = 3.5): Real;  // syntax error

Parameter mit Standardwerten müssen am Ende der Parameterliste angegeben werden. Sobald einem Parameter ein Standardwert zugewiesen wurde, müssen Sie auch allen folgenden Parametern Standardwerte zuweisen. Die folgende Deklaration ist aus diesem Grund nicht zulässig:

procedure MyProcedure(I: Integer = 1; S: string);  // syntax error

In einem prozeduralen Typ angegebene Standardwerte überschreiben die in einer Routine angegebenen Standardwerte. In den Deklarationen

type TResizer = function(X: Real; Y: Real = 1.0): Real;
function Resizer(X: Real; Y: Real = 2.0): Real;
var
  F: TResizer;
  N: Real;

führen die Anweisungen:

F := Resizer;
F(N);

zur Übergabe der Werte (N, 1.0) an Resizer.

Für Standardparameter dürfen nur Werte verwendet werden, die in Form eines Konstantenausdrucks angegeben werden können. Für prozedurale Parameter oder Parameter vom Typ dynamisches Array, Klasse, Klassenreferenz oder Interface kann deshalb nur der Standardwert nil verwendet werden. Für Parameter vom Typ Record, Variant, Datei, statisches Array oder Objekt sind keine Standardwerte zulässig.

Standardparameter und überladene Funktionen

Wenn Sie Standardparameterwerte in überladenen Routinen einsetzen, müssen Sie mehrdeutige Parametersignaturen vermeiden. Beispiel:

procedure Confused(I: Integer); overload;
   ...
procedure Confused(I: Integer; J: Integer = 0); overload;
   ...
Confused(X);   //  Which procedure is called?

Tatsächlich wird keine der beiden Prozeduren aufgerufen. Diese Zeilen führen zu einem Compilierungsfehler.

Standardparameter in forward- und interface-Deklarationen

Wenn eine Routine eine forward-Deklaration enthält oder im interface-Abschnitt einer Unit enthalten ist, können Sie die Standardparameterwerte nur in der forward- bzw. interface-Deklaration angeben. Standardwerte in der definierenden (Implementierungs-)Deklaration werden ignoriert. Liegt für eine Routine keine forward- oder interface-Deklaration vor, kann die definierende Deklaration Standardparameterwerte angeben.

Siehe auch