Prozedurale Typen (Object Pascal)

Aus Appmethod Topics
Wechseln zu: Navigation, Suche

Nach oben zu Datentypen, Variablen und Konstanten - Index


Prozeduren und Funktionen können als Wert betrachtet und einer Variablen zugewiesen werden, sodass sie als Parameter übergeben werden können. Dieses Verfahren wird durch so genannte prozedurale Typen ermöglicht.

In diesem Thema wird der neuere prozedurale Typ ("Referenz auf eine Prozedur"), der mit anonymen Methoden verwendet wird, nicht behandelt. Siehe Anonyme Methoden in Object Pascal.

Allgemeines zu prozeduralen Typen

Das folgende Beispiel zeigt die Verwendung eines prozeduralen Typs. Angenommen, Sie definieren eine Funktion mit dem Namen Calc, die zwei Integer-Parameter übernimmt und einen Integer-Wert zurückgibt:

 
  function Calc(X,Y: Integer): Integer;

Sie können die Funktion Calc der Variablen F zuweisen:

 
  var F: function(X,Y: Integer): Integer;
  F := Calc;

Wenn Sie im Kopf einer Prozedur oder Funktion den Bezeichner nach dem Wort procedure bzw. function entfernen, erhalten Sie den rechten Teil der Deklaration eines prozeduralen Typs. Dieser Typname kann direkt in einer Variablendeklaration (siehe oben) oder zur Deklaration neuer Typen verwendet werden:

 
  type
    TIntegerFunction = function: Integer;
    TProcedure = procedure;
    TStrProc = procedure(const S: string);
    TMathFunc = function(X: Double): Double;
  var
    F: TIntegerFunction; // F is a parameterless function that returns an integer
    Proc: TProcedure;    // Proc is a parameterless procedure
    SP: TStrProc;        // SP is a procedure that takes a string parameter
    M: TMathFunc;        // M is a function that takes a Double (real)
                         // parameter and returns a Double
  
    procedure FuncProc(P: TIntegerFunction);  // FuncProc is a procedure
                         // whose only parameter is a parameterless
                         // integer-valued function

Methodenzeiger

Alle oben aufgeführten Variablen sind sogenannte Prozedurzeiger, also Zeiger auf die Adresse einer Prozedur oder Funktion. Um eine Methode eines Instanzobjekts zur referenzieren (siehe Klassen und Objekte), muss dem Namen des prozeduralen Typs die Klausel of object hinzugefügt werden. Zum Beispiel:

 
  type
    TMethod      = procedure of object;
    TNotifyEvent = procedure(Sender: TObject) of object;

Diese Typen sind Methodenzeiger. Ein Methodenzeiger wird in Form zweier Zeiger codiert, von denen der erste die Adresse der Methode speichert, während der zweite eine Referenz auf das Objekt enthält, zu dem die Methode gehört. Nach den Deklarationen:

 
  type
    TNotifyEvent = procedure(Sender: TObject) of object;
    TMainForm = class(TForm)
      procedure ButtonClick(Sender: TObject);
       ...
    end;
  var
    MainForm: TMainForm;
    OnClick: TNotifyEvent

ist die folgende Zuweisung korrekt:

 
  OnClick := MainForm.ButtonClick;

Zwei prozedurale Typen sind kompatibel, wenn sie folgende Bedingungen erfüllen:

  • Sie verwenden dieselbe Aufrufkonvention.
  • Sie haben denselben (oder keinen) Rückgabetyp.
  • Sie verwenden die gleiche Anzahl Parameter identischen Typs an den entsprechenden Positionen (die Parameternamen sind nicht von Bedeutung).

Prozedurale Zeigertypen sind immer inkompatibel mit Methodenzeigertypen. Der Wert nil kann jedem prozeduralen Typ zugewiesen werden.

Verschachtelte Prozeduren und Funktionen (Routinen, die in anderen Routinen deklariert sind), können nicht als prozedurale Werte verwendet werden. Dasselbe gilt für vordefinierte Prozeduren und Funktionen (Standardroutinen). Wenn Sie eine Standardroutine wie Length als prozeduralen Wert verwenden möchten, müssen Sie die Routine gewissermaßen "verpacken":

 
  function FLength(S: string): Integer;
  begin
    Result := Length(S);
  end;

Prozedurale Typen in Anweisungen und Ausdrücken

Wenn links in einer Zuweisung eine prozedurale Variable steht, erwartet der Compiler auf der rechten Seite einen prozeduralen Wert. Durch die Zuweisung wird aus der Variablen auf der linken Seite ein Zeiger auf die Funktion oder Prozedur auf der rechten Seite. In einem anderen Kontext führt die Verwendung einer prozeduralen Variable zu einem Aufruf der referenzierten Prozedur oder Funktion. Prozedurale Variablen können auch als Parameter übergeben werden:

 
  var
    F: function(X: Integer): Integer;
    I: Integer;
    function SomeFunction(X: Integer): Integer;
      ...
    F := SomeFunction;    // assign SomeFunction to F
    I := F(4);            // call function; assign result to I

In Zuweisungen bestimmt der Typ der Variable auf der linken Seite, wie die Prozedur oder Methode auf der rechten Seite interpretiert wird. Zum Beispiel:

 
  var
    F, G: function: Integer;
    I: Integer;
    function SomeFunction: Integer;
      ...
    F := SomeFunction;      // assign SomeFunction to F
    G := F;                 // copy F to G
    I := G;                 // call function; assign result to I

Die erste Anweisung weist F einen prozeduralen Wert zu. Die zweite Anweisung kopiert diesen Wert in eine andere Variable. In der dritten Anweisung wird die referenzierte Funktion aufgerufen und das Ergebnis I zugewiesen. Da I keine prozedurale, sondern eine Integer-Variable ist, führt die letzte Zuweisung zum Aufruf der Funktion (die als Ergebnis einen Integer-Wert zurückgibt).

Es gibt Situationen, in denen nicht offensichtlich ist, wie eine prozedurale Variable interpretiert werden muss. Sehen Sie sich dazu folgende Anweisung an:

if F = MyFunction then ...;

In diesem Fall führt F zum Aufruf einer Funktion. Der Compiler ruft zuerst die Funktion, die von F referenziert wird, und dann die Funktion MyFunction auf. Anschließend werden die Ergebnisse verglichen. Hier gilt folgende Regel: Eine prozedurale Variable innerhalb eines Ausdrucks stellt einen Aufruf der referenzierten Prozedur oder Funktion dar. Referenziert F eine Prozedur (die keinen Wert als Ergebnis liefert) oder eine Funktion, an die Parameter übergeben werden müssen, führt die obige Anweisung zu einem Compiler-Fehler. Um den prozeduralen Wert von F mit MyFunction zu vergleichen, verwenden Sie folgende Anweisung:

 
  if @F = @MyFunction then ...;

@F konvertiert F in eine untypisierte Zeigervariable, die eine Adresse enthält, und @MyFunction liefert die Adresse von MyFunction.

Um anstelle der in einer prozeduralen Variable gespeicherten Adresse ihre Speicheradresse zu erhalten, verwenden Sie @@. So liefert beispielsweise @@F die Adresse von F.

Der Operator @ kann auch verwendet werden, um einer prozeduralen Variablen einen untypisierten Zeigerwert zuzuweisen. Zum Beispiel:

 
  var StrComp: function(Str1, Str2: PChar): Integer;
     ...
  @StrComp := GetProcAddress(KernelHandle, 'lstrcmpi');

Diese Anweisung ruft die Funktion GetProcAddress auf und weist StrComp das Ergebnis zu.

Jede prozedurale Variable kann den Wert nil enthalten, was bedeutet, dass sie auf keine bestimmte Adresse zeigt. Der Aufruf einer prozeduralen Variable, die den Wert nil enthält, führt zu einem Fehler. Mit der Standardfunktion Assigned können Sie feststellen, ob einer prozeduralen Variablen ein Wert zugewiesen ist:

 
  if Assigned(OnClick) then OnClick(X);

Siehe auch