Beiträge durchsuchen

Funktions- und Methodenzeiger

Funktionen und Prozeduren können selbst in Variablen gespeichert werden (bzw. ein Verweis auf sie). Somit ist es möglich, einer Funktion eine andere Funktion als Parameter zu übergeben.

type
 TMeineFunktion = function: Integer;

In diesem Fall haben die Funktionen oder Prozeduren keinen Namen, sondern bestehen aus dem Schlüsselwort function oder procedure, bei Bedarf gefolgt von Parametern in runden Klammern und einem Rückgabewert. Handelt es sich um Methoden, folgt noch „of object“:

 TMeineMethode = procedure of object;

Praktischer Nutzen der Sache: Man kann somit eine Ereignisbehandlungsroutine erstellen und erst zur Laufzeit einem Objekt (einer Komponente) zuordnen.
Beispiel:

type
 TMyOnClick = procedure(Sender: TObject) of object;
 TMyForm = class(TForm)
 procedure MyButtonClickEvent(Sender: TObject);
 end;
var
MyOnClick: TMyOnClick;
MyForm: TMyForm;
...
MyOnClick := MyForm.MyButtonClickEvent;

Die OnClick-Events der Komponenten haben ebenfalls einen Methodenzeiger als Typ, den gleichen wie TMyOnClick im Beispiel. Deshalb ist es möglich, z.B. einem Button zur Laufzeit eine Methode mit dem gleichen Parameter zuzuweisen. Variablen vom Typ einer Funktion/Prozedur beinhalten die Speicheradresse, also einen Zeiger auf die Routine. Bei Methoden werden zwei Zeiger gespeichert: die Adresse der Methode und eine Referenz auf die Instanz.

Was ist ein Methodenzeiger? Genau genommen ist ein Methodenzeiger eine Referenz auf eine Methode einer Instanz einer Klasse. Das klingt jetzt erstmal kompliziert. Es handelt sich hierbei aber um das einfache „procedure() of object“. Ein solcher Methodezeiger kann  jede Methode eines jeden Objekts aufnehmen, die dieselbe Signatur (Rückgabewert, Parameter) besitzt.

Doch wie schafft es Delphi, dass die Methode für die passende Instanz aufgerufen wird? In einem Zeiger ist ja nur Platz für die Adresse der Methode oder des Objekts, nicht aber für beide. Delphi geht hier wieder einmal sehr trickreich vor. In Wirklichkeit ist ein Methodenzeiger gar kein Zeiger, sondern ein Record vom Typ TMethod. Dieser Record enthält zwei Felder. TMethod.Code enthält die Adresse der Methode und TMethod.Data einen Zeiger auf das Objekt, an das die Methode gebunden ist. Beim Aufruf eines Methodenzeigers übergibt Delphi die in TMethod.Data liegende Referenz als ersten versteckten Self-Parameter an die in TMethode.Code verankerte Methode, woduch die Methode für das richtige Objekt aufrufen wird.
Mit diesem Wissen kann man Delphi nun austricksen und die Methodenzeiger für andere Dinge einsetzen. Zum Beispiel ist es möglich eine gewöhnliche Prozedur als Methode zu missbrauchen. Die Prozedur muss in diesem Fall dieselbe Signatur wie der Methodenzeiger haben. Zudem muss aber noch der versteckte Self-Parameter als erster Parameter eintragen sein. Danach ist es nur noch eine Sache des richtigen Typecasts, der die Prozedur in einen Methodenzeiger packt.

procedure SimulatedMethod(Self: TForm1; Sender: TObject);
begin
 Self.Caption := 'Du hast mich aufgerufen';
end;

procedure TForm1.FormCreate(Sender: TObject);
var
 Event: TNotifyEvent;
begin
 TMethod(Event).Code := @SimulatedMethod;
 TMethod(Event).Data := Self;
 Button1.OnClick := Event;
end;

Mit dieser Technik kann man auch die Methode bzw. das Objekt austauschen.