Methoden (Delphi)
Nach oben zu Klassen und Objekte - Index
Inhaltsverzeichnis
Eine Methode ist eine Prozedur oder Funktion, die zu einer bestimmten Klasse gehört. Daher wird beim Aufruf einer Methode das Objekt (bzw. bei einer Klassenmethode die Klasse) angegeben, mit dem die Operation durchgeführt werden soll. SomeObject.Free ruft beispielsweise die Methode Free in SomeObject auf.
In diesem Thema wird Folgendes behandelt:
- Methodendeklarationen und -implementierungen
- Methodenbindung
- Methoden überladen
- Konstruktoren und Destruktoren
- Botschaftsmethoden
Allgemeines zu Methoden
Methoden werden in der Deklaration einer Klasse als Prozedur- und Funktionsköpfe angegeben, die wie Vorwärtsdeklarationen (forward) arbeiten. Die jeweilige Methode muss dann nach der Klassendefinition in demselben Modul durch eine definierende Deklaration implementiert werden. Die Deklaration der Klasse TMyClass im folgenden Beispiel enthält eine Methode mit dem Namen DoSomething:
type
TMyClass = class(TObject)
...
procedure DoSomething;
...
end;
Weiter unten in demselben Modul wird die definierende Deklaration für DoSomething implementiert:
procedure TMyClass.DoSomething;
begin
...
end;
Während eine Klasse im interface- oder implementation-Abschnitt einer Unit deklariert werden kann, müssen die definierenden Deklarationen ihrer Methoden im implementation-Abschnitt stehen.
Im Kopf einer definierenden Deklaration wird der Name der Methode immer mit der Klasse qualifiziert, zu der die Methode gehört. Optional kann auch die Parameterliste aus der Klassendeklaration wiederholt werden, doch müssen in diesem Fall Reihenfolge, Typ und Namen der Parameter genau übereinstimmen. Bei einer Funktion muss auch der Rückgabewert identisch sein.
Methodendeklarationen können spezielle Direktiven enthalten, die in anderen Funktionen oder Prozeduren nicht verwendet werden. Direktiven müssen in der Klassendeklaration enthalten sein (nicht in der definierenden Deklaration) und in der folgenden Reihenfolge angegeben werden:
reintroduce; overload; Bindung; Aufrufkonvention; abstract; Warnung
Beschreibung:
- Bindung ist virtual, dynamic oder override.
- Die Aufrufkonvention lautet register, pascal, cdecl, stdcall oder safecall;
- Warnung ist platform, deprecated oder library. Weitere Informationen zu diesen Warnungs-Direktiven finden Sie unter Hinweis-Direktiven.
Alle Delphi-Direktiven sind unter Direktiven aufgelistet.
Inherited
Das reservierte Wort inherited ist für die Implementierung von polymorphem Verhalten von großer Bedeutung. Es kann in Methodendefinitionen (mit oder ohne nachfolgendem Bezeichner) angegeben werden.
Folgt auf inherited ein Member-Name, entspricht dies einem normalen Methodenaufruf bzw. einer Referenz auf eine Eigenschaft oder ein Feld. Der einzige Unterschied besteht darin, dass die Suche nach dem referenzierten Member bei dem direkten Vorfahren der Klasse beginnt, zu der die Methode gehört. Wenn Sie beispielsweise
inherited Create(...);
in der Definition einer Methode angeben, wird die geerbte Methode Create aufgerufen.
inherited ohne Bezeichner verweist auf die geerbte Methode mit demselben Namen wie die aufrufende Methode. Handelt es sich bei der aufrufenden Methode um eine Botschaftsbehandlung, verweist diese auf die geerbte Botschaftsbehandlung für dieselbe Botschaft. In diesem Fall übernimmt inherited keine expliziten Parameter, sondern der geerbten Methode werden einfach die Parameter der aufrufenden Methode übergeben. Zum Beispiel wird
inherited;
häufig in der Implementierung von Konstruktoren verwendet. Der geerbte Konstruktor wird mit den Parametern aufgerufen, die an die abgeleitete Klasse übergeben wurden.
Self
Der Bezeichner Self verweist in der Implementierung einer Methode auf das Objekt, in dem die Methode aufgerufen wird. Das folgende Beispiel zeigt die Methode Add der Klasse TCollection in der Unit Classes:
function TCollection.Add: TCollectionItem;
begin
Result := FItemClass.Create(Self);
end;
Add ruft die Methode Create der Klasse auf, die das Feld FItemClass referenziert (ist immer ein Nachkomme von TCollectionItem). Da an TCollectionItem.Create ein einzelner Parameter des Typs TCollection übergeben wird, übergibt Add die Instanz von TCollection, in der Add aufgerufen wird. Der folgende Code veranschaulicht dies:
var MyCollection: TCollection;
...
MyCollection.Add // MyCollection is passed to the
// TCollectionItem.Create method
Self ist in vielen Situationen hilfreich. So kann beispielsweise ein Member-Bezeichner, der in einem Klassentyp deklariert ist, in einer Methode dieser Klasse erneut deklariert werden. In diesem Fall kann mit Self.Identifier auf den ursprünglichen Member-Bezeichner zugegriffen werden.
Informationen zu Self in Klassenmethoden finden Sie unter "Klassenoperatoren" in Klassenreferenzen.
Methodenbindung
Methodenbindungen können static (statisch, Standard), virtual (virtuell) oder dynamic (dynamisch) sein. Virtuelle und dynamische Methoden können überschrieben werden, und sie können abstrakt sein. Diese Angaben spielen eine Rolle, wenn eine Variable eines bestimmten Klassentyps eine Instanz einer abgeleiteten Klasse enthält. Sie bestimmen dann, welche Implementierung beim Aufruf der Methode aktiviert wird.
Statische Methoden
Methoden sind standardmäßig statisch. Beim Aufruf bestimmt der deklarierte Typ (also der Typ zur Compilierzeit) der im Aufruf verwendeten Klassen- bzw. Objektvariable, welche Implementierung aktiviert wird. Die Draw-Methoden im folgenden Beispiel sind statisch:
type
TFigure = class
procedure Draw;
end;
TRectangle = class(TFigure)
procedure Draw;
end;
Ausgehend von diesen Deklarationen zeigt das folgende Beispiel, wie sich Aufrufe statischer Methoden auswirken. Im zweiten Aufruf von Figure.Draw referenziert die Variable Figure ein Objekt der Klasse TRectangle. Es wird jedoch die Draw-Implementierung in TFigure aufgerufen, weil Figure als TFigure deklariert ist:
var
Figure: TFigure;
Rectangle: TRectangle;
begin
Figure := TFigure.Create;
Figure.Draw; // calls TFigure.Draw
Figure.Destroy;
Figure := TRectangle.Create;
Figure.Draw; // calls TFigure.Draw
TRectangle(Figure).Draw; // calls TRectangle.Draw
Figure.Destroy;
Rectangle := TRectangle.Create;
Rectangle.Draw; // calls TRectangle.Draw
Rectangle.Destroy;
end;
Virtuelle und dynamische Methoden
Mithilfe der Direktiven virtual und dynamic können Methoden als virtual oder dynamic deklariert werden. Virtuelle und dynamische Methoden können im Gegensatz zu statischen Methoden in abgeleiteten Klassen überschrieben werden. Beim Aufrufen einer überschriebenen Methode bestimmt nicht der deklarierte, sondern der aktuelle Typ (also der Typ zur Laufzeit) der im Aufruf verwendeten Klassen- bzw. Objektvariable, welche Implementierung aktiviert wird.
Um eine Methode zu überschreiben, wird sie mit der Direktive override erneut deklariert. Bei einer override-Deklaration müssen Reihenfolge und Typ der Parameter sowie der Typ des Rückgabewertes (falls vorhanden) mit der Deklaration in der Vorfahrklasse übereinstimmen.
Im folgenden Beispiel wird die in der Klasse TFigure deklarierte Methode Draw in zwei abgeleiteten Klassen überschrieben:
type
TFigure = class
procedure Draw; virtual;
end;
TRectangle = class(TFigure)
procedure Draw; override;
end;
TEllipse = class(TFigure)
procedure Draw; override;
end;
Ausgehend von diesen Deklarationen zeigt der folgende Programmcode, wie sich der Aufruf einer virtuellen Methode durch eine Variable auswirkt, deren aktueller Typ zur Laufzeit geändert wird:
var
Figure: TFigure;
begin
Figure := TRectangle.Create;
Figure.Draw; // calls TRectangle.Draw
Figure.Destroy;
Figure := TEllipse.Create;
Figure.Draw; // calls TEllipse.Draw
Figure.Destroy;
end;
Nur virtuelle und dynamische Methoden können überschrieben werden. Alle Methoden können jedoch überladen werden (siehe Methoden überladen.
Finale Methoden
Der Delphi-Compiler unterstützt auch das Konzept der finalen virtuellen und dynamischen Methoden. Deklarationen der finalen Methoden haben die Form:
function|procedure FunctionName; virtual|dynamic; final;
Hier wird mit der Syntax virtual|dynamic
(zwei Schlüsselwörter und das Pipe-Zeichen |
dazwischen) festgelegt, dass nur eins der Schlüsselwörter virtual oder dynamic verwendet werden sollte. Nur die Schlüsselwörter virtual oder dynamic sind bedeutend; das Pipe-Zeichen selbst sollte gelöscht werden.
Durch das Schlüsselwort final bei einer virtuellen oder dynamischen Methode kann verhindert werden, dass diese von einer abgeleiteten Klasse überschrieben wird. Mit dem Schlüsselwort final wird gleichzeitig dokumentiert, auf welche Weise die Klasse verwendet werden soll. Außerdem ermöglicht es dem Compiler eine Optimierung des generierten Codes.
Hinweis: Die Schlüsselwörter virtual oder dynamic müssen vor dem Schlüsselwort final geschrieben werden.
Beispiel
type
Base = class
procedure TestProcedure; virtual;
procedure TestFinalProcedure; virtual; final;
end;
Derived = class(Base)
procedure TestProcedure; override;
//Ill-formed: E2352 Cannot override a final method
procedure TestFinalProcedure; override;
end;
Unterschiede zwischen virtuellen und dynamischen Methoden
In Delphi für Win32 sind virtuelle und dynamische Methoden von der Semantik her identisch. Sie unterscheiden sich aber bei der Implementierung der Aufrufweiterleitung zur Laufzeit: Virtuelle Methoden werden hinsichtlich der Geschwindigkeit, dynamische Methoden hinsichtlich der Codegröße optimiert.
Im Allgemeinen kann mit virtuellen Methoden polymorphes Verhalten am effizientesten implementiert werden. Dynamische Methoden sind hilfreich, wenn in einer Basisklasse eine große Anzahl überschreibbarer Methoden deklariert ist, die von vielen abgeleiteten Klassen geerbt, aber nur selten überschrieben werden.
Hinweis: Verwenden Sie dynamische Methoden nur, wenn sich daraus ein nachweisbarer Nutzen ergibt. Im Allgemein sollten Sie eher virtuelle Methoden verwenden.
Unterschiede zwischen Überschreiben und Verdecken
Wenn in einer Methodendeklaration dieselbe Bezeichner- und Parametersignatur wie bei einer geerbten Methode ohne die Anweisung override angegeben wird, wird die geerbte Methode durch die neue Deklaration nur verdeckt, nicht überschrieben. Beide Methoden sind jedoch in der abgeleiteten Klasse vorhanden, in der der Methodenname statisch gebunden wird. Zum Beispiel:
type
T1 = class(TObject)
procedure Act; virtual;
end;
T2 = class(T1)
procedure Act; // Act is redeclared, but not overridden
end;
var
SomeObject: T1;
begin
SomeObject := T2.Create;
SomeObject.Act; // calls T1.Act
end;
Reintroduce
Die Direktive reintroduce unterdrückt Compiler-Warnungen, wenn zuvor deklarierte virtuelle Methoden verdeckt werden. Zum Beispiel:
procedure DoSomething; reintroduce; // The ancestor class also
// has a DoSomething method
Verwenden Sie reintroduce, wenn eine geerbte virtuelle Methode durch eine neue Deklaration verdeckt werden soll.
Abstrakte Methoden
Eine abstrakte Methode ist eine virtuelle oder dynamische Methode, die nicht in der Klasse implementiert ist, in der sie deklariert wird. Die Implementierung wird erst später in einer abgeleiteten Klasse durchgeführt. Bei der Deklaration abstrakter Methoden muss die Anweisung abstract nach virtual oder dynamic angegeben werden. Zum Beispiel:
procedure DoSomething; virtual; abstract;
Eine abstrakte Methode kann nur in einer Klasse oder Instanz einer Klasse aufgerufen werden, in der sie überschrieben wurde.
Klassenmethoden
Die meisten Methoden werden Instanzmethoden genannt, weil sie mit einer einzelnen Instanz eines Objekts arbeiten. Eine Klassenmethode ist eine Methode, die nicht mit Objekten, sondern mit Klassen arbeitet. Es gibt zwei Typen von Klassenmethoden: reguläre Klassenmethoden und klassenstatische Methoden.
Reguläre Klassenmethoden
Die Definition muss mit dem reservierten Wort class beginnen. Zum Beispiel:
type
TFigure = class
public
class function Supports(Operation: string): Boolean; virtual;
class procedure GetInfo(var Info: TFigureInfo); virtual;
...
end;
Auch die definierende Deklaration einer Klassenmethode muss mit class eingeleitet werden. Zum Beispiel:
class procedure TFigure.GetInfo(var Info: TFigureInfo);
begin
...
end;
In der definierenden Deklaration einer Klassenmethode kann mit dem Bezeichner Self auf die Klasse zugegriffen werden, in der die Methode aufgerufen wird (dies kann auch ein Nachkomme der Klasse sein, in der sie definiert ist). Wird die Methode beispielsweise in der Klasse C aufgerufen, hat Self den Typ class of C. Daher können mit Self nicht auf Instanzfelder, Instanzeigenschaften und normale (Objekt-)Methoden zugreifen, sondern Self kann nur für Aufrufe von Konstruktoren und anderen Klassenmethoden oder für den Zugriff auf Klasseneigenschaften und Klassenfelder verwendet werden.
Eine Klassenmethode kann über eine Klassenreferenz oder eine Objektreferenz aufgerufen werden. Bei einer Objektreferenz erhält Self als Wert die Klasse des betreffenden Objekts.
Klassenstatische Methoden
Auf klassenstatische Methoden kann, wie auf Klassenmethoden, ohne Objektreferenz zugegriffen werden. Im Gegensatz zu regulären Klassenmethoden haben klassenstatische Methoden keinen Self-Parameter. Sie können außerdem auf keine Instanz-Member zugreifen. (Sie können aber auf Klassenfelder, Klasseneigenschaften und Klassenmethoden zugreifen.) Wiederum im Gegensatz zu Klassenmethoden können klassenstatische Methoden nicht als virtual deklariert werden.
Um eine Methode als klassenstatisch zu deklarieren, fügen Sie das Wort static an die Deklaration an:
type
TMyClass = class
strict private
class var
FX: Integer;
strict protected
// Note: Accessors for class properties
// must be declared class static.
class function GetX: Integer; static;
class procedure SetX(val: Integer); static;
public
class property X: Integer read GetX write SetX;
class procedure StatProc(s: String); static;
end;
Wie eine Klassenmethode kann eine klassenstatische Methode über den Klassentyp (also ohne Objektreferenz) aufgerufen werden, z. B.:
TMyClass.X := 17;
TMyClass.StatProc('Hello');
Methoden überladen
Eine Methode kann mit der Direktive overload neu deklariert werden. Wenn sich die Parametersignatur von der ihres Vorfahren unterscheidet, wird die geerbte Methode überladen, ohne dass sie dadurch verdeckt wird. Bei einem Aufruf der Methode in einer abgeleiteten Klasse wird dann diejenige Implementierung aktiviert, bei der die Parameter übereinstimmen.
Verwenden Sie beim Überladen einer virtuellen Methode die Direktive reintroduce, wenn die Methode in einer abgeleiteten Klasse neu deklariert wird. Zum Beispiel:
type
T1 = class(TObject)
procedure Test(I: Integer); overload; virtual;
end;
T2 = class(T1)
procedure Test(S: string); reintroduce; overload;
end;
...
SomeObject := T2.Create;
SomeObject.Test('Hello!'); // calls T2.Test
SomeObject.Test(7); // calls T1.Test
Innerhalb einer Klasse dürfen nicht mehrere überladene Methoden mit demselben Namen als published deklariert werden. Zur Pflege von Laufzeit-Typinformationen wird für jeden als published deklarierten Member ein eindeutiger Name benötigt:
type
TSomeClass = class
published
function Func(P: Integer): Integer;
function Func(P: Boolean): Integer; // error
...
Methoden, die als read- oder write-Bezeichner für Eigenschaften dienen, können nicht überladen werden.
Bei der Implementierung einer überladenen Methode muss die Parameterliste aus der Klassendeklaration wiederholt werden. Weitere Informationen zum Überladen finden Sie unter Prozeduren und Funktionen überladen in Prozeduren und Funktionen (Delphi).
Konstruktoren
Ein Konstruktor ist eine spezielle Methode, mit der Instanzobjekte erstellt und initialisiert werden. Die Deklaration gleicht einer normalen Prozedurdeklaration, beginnt aber mit dem Wort constructor. Beispiele:
constructor Create;
constructor Create(AOwner: TComponent);
Für Konstruktoren muss die Standard-Aufrufkonvention register verwendet werden. Obwohl die Deklaration keinen Rückgabewert enthält, gibt ein Konstruktor immer einen Verweis auf das Objekt zurück, das er erstellt bzw. in dem er aufgerufen wird.
Eine Klasse kann auch mehrere Konstruktoren haben. Im Normalfall ist jedoch nur einer vorhanden. Konstruktoren heißen normalerweise immer Create.
Das folgende Beispiel zeigt, wie Sie ein Objekt durch einen Aufruf des Konstruktors eines Klassentyps erstellen können:
MyObject := TMyClass.Create;
Diese Anweisung reserviert zuerst Speicher für das neue Objekt. Anschließend wird allen Ordinalfeldern der Wert null, allen Zeigern und Klassentypfeldern der Wert nil und allen String-Feldern ein leerer String zugewiesen. Danach werden die weiteren Aktionen in der Implementierung des Konstruktors ausgeführt (z. B. Initialisieren der Objekte mit den als Parameter übergebenen Werten). Am Ende gibt der Konstruktor eine Referenz auf das neu erstellte und initialisierte Objekt zurück. Der Typ entspricht dem im Aufruf angegebenen Klassentyp.
Tritt in einem mit einer Klassenreferenz aufgerufenen Konstruktor eine Exception auf, wird das unvollständige Objekt automatisch durch einen Aufruf des Destruktors Destroy freigegeben.
Wenn Sie einen Konstruktor mit einer Objektreferenz (anstatt mit einer Klassenreferenz) aufrufen, wird kein Objekt erstellt. Stattdessen werden, wie bei einer normalen Routine, die angegebenen Anweisungen mit dem Objekt ausgeführt, und es wird eine Referenz auf das Objekt zurückgegeben. Beim Aufruf mit einer Objektreferenz wird normalerweise der geerbte Konstruktor mit inherited ausgeführt.
Das folgende Beispiel zeigt einen Klassentyp und den zugehörigen Konstruktor:
type
TShape = class(TGraphicControl)
private
FPen: TPen;
FBrush: TBrush;
procedure PenChanged(Sender: TObject);
procedure BrushChanged(Sender: TObject);
public
constructor Create(Owner: TComponent); override;
destructor Destroy; override;
...
end;
constructor TShape.Create(Owner: TComponent);
begin
inherited Create(Owner); // Initialize inherited parts
Width := 65; // Change inherited properties
Height := 65;
FPen := TPen.Create; // Initialize new fields
FPen.OnChange := PenChanged;
FBrush := TBrush.Create;
FBrush.OnChange := BrushChanged;
end;
Als erste Anweisung wird in der Regel immer der geerbte Konstruktor aufgerufen, um die geerbten Felder zu initialisieren. Danach werden den in der abgeleiteten Klasse deklarierten Feldern Werte zugewiesen. Da der Konstruktor grundsätzlich den Speicherbereich bereinigt, der dem neuen Objekt zugewiesen wird, erhalten alle Felder automatisch den Anfangswert null (Ordinaltypen), nil (Zeiger und Klassentypen), einen leeren String (String-Typen) oder Unassigned (Varianten). Aus diesem Grund brauchen nur solche Felder explizit initialisiert zu werden, denen ein Anfangswert ungleich null (bzw. kein leerer String) zugewiesen werden soll.
Ein als virtual deklarierter Konstruktor, der mit einem Klassentypbezeichner aufgerufen wird, entspricht einem statischen Konstruktor. In Verbindung mit Klassenreferenztypen können jedoch durch virtuelle Konstruktoren Objekte polymorph erstellt werden (d. h. der Objekttyp ist beim Compilieren noch nicht bekannt). Weitere Informationen finden Sie unter Klassenreferenzen.
Destruktoren
Ein Destruktor ist eine spezielle Methode, die ein Objekt im Speicher freigibt. Die Deklaration gleicht einer Prozedurdeklaration, beginnt aber mit dem Wort destructor. Beispiel:
destructor SpecialDestructor(SaveData: Boolean);
destructor Destroy; override;
In Win32 muss für Destruktoren die Standard-Aufrufkonvention register verwendet werden. Obwohl in einer Klasse mehrere Destruktoren implementiert werden können, ist es ratsam, nur die geerbte Methode Destroy zu überschreiben und keine weiteren Destruktoren zu deklarieren.
Ein Destruktor kann nur über ein Instanzobjekt aufgerufen werden. Zum Beispiel:
MyObject.Destroy;
Beim Aufruf eines Destruktors werden zuerst die in der Implementierung angegebenen Aktionen ausgeführt. Normalerweise werden hier untergeordnete Objekte und zugewiesene Ressourcen freigegeben. Danach wird der durch das Objekt belegte Speicherplatz freigegeben.
Das folgende Beispiel zeigt eine typische Destruktorimplementierung:
destructor TShape.Destroy;
begin
FBrush.Free;
FPen.Free;
inherited Destroy;
end;
Die letzte Anweisung ruft den geerbten Destruktor auf, der die geerbten Felder freigibt.
Wenn beim Erstellen eines Objekts eine Exception auftritt, wird das unvollständige Objekt automatisch durch einen Aufruf von Destroy freigegeben. Destroy muss daher auch in der Lage sein, Objekte freizugeben, die nur teilweise erstellt wurden. Da im Konstruktor alle Felder eines neuen Objekts zuerst mit null oder leeren Werten initialisiert werden, haben Klassen- und Zeigerfelder in einer unvollständigen Instanz immer den Wert nil. Testen Sie solche Felder im Destruktor immer auf den Wert nil, bevor Sie Operationen mit ihnen durchführen. Wenn Sie Objekte nicht mit Destroy, sondern mit der Methode Free (in TObject definiert) freigeben, wird diese Prüfung automatisch durchgeführt.
Klassenkonstruktoren
Ein Klassenkonstruktor ist eine spezielle Klassenmethode, auf die von Entwicklern nicht zugegriffen werden kann. Aufrufe von Klassenkonstruktoren werden automatisch vom Compiler in den initialization-Abschnitt der Unit eingefügt, in der die Klasse definiert ist. In der Regel werden mit Klassenkonstruktoren die statischen Felder der Klasse initialisiert oder Initialisierungen ausgeführt, die erforderlich sind, damit die Klasse oder eine Klasseninstanz ordnungsgemäß arbeiten kann. Dasselbe Ergebnis kann zwar auch durch Einfügen des Klasseninitialisierungscodes in den initialization-Abschnitt erzielt werden, aber Klassenkonstruktoren unterstützen den Compiler bei der Entscheidung, welche Klassen in die endgültige Binärdatei aufgenommen und welche daraus entfernt werden sollen.
Das nächste Beispiel zeigt eine normale Initialisierung von Klassenfeldern:
type
TBox = class
private
class var FList: TList<Integer>;
end;
implementation
initialization
{ Initialize the static FList member }
TBox.FList := TList<Integer>.Create();
end.
Dieses Vorgehen hat einen entscheidenden Nachteil: Auch wenn eine Anwendung die Unit einbeziehen kann, in der TBox deklariert ist, kann es sein, dass die Klasse TBox tatsächlich nie verwendet wird. Im obigen Beispiel wird die Klasse TBox in die resultierende Binärdatei einbezogen, weil sie im initialization-Abschnitt referenziert wird. Mit Klassenkonstruktoren kann dieses Problem umgangen werden:
type
TBox = class
private
class var FList: TList<Integer>;
class constructor Create;
end;
implementation
class constructor TBox.Create;
begin
{ Initialize the static FList member }
FList := TList<Integer>.Create();
end;
end.
In diesem Beispiel überprüft der Compiler, ob TBox tatsächlich in der Anwendung verwendet wird. Falls ja, wird automatisch ein Aufruf des Klassenkonstruktors dem initialization-Abschnitt der Unit hinzugefügt.
Hinweis: In der Regel sorgt der Compiler für die richtige Reihenfolge der Initialisierung von Klassen, in einigen komplexen Szenarien könnte die Reihenfolge aber zufällig werden. Und zwar dann, wenn der Klassenkonstruktor einer Klasse vom Status einer anderen Klasse abhängig ist, der wiederum von der ersten Klasse abhängt.
Hinweis: Der Klassenkonstruktor für eine generische Klasse oder einen Record kann mehrfach ausgeführt werden. Die genaue Anzahl der Ausführungen des Klassenkonstruktors hängt in diesem Fall von der Anzahl der spezialisierten Versionen des generischen Typs ab. Der Klassenkonstruktor für eine spezialisierte TList<String>-Klasse kann beispielsweise mehrfach in derselben Anwendung ausgeführt werden.
Klassendestruktoren
Klassendestruktoren sind das Gegenteil von Klassenkonstruktoren, und zwar weil sie die Finalisierung der Klasse vornehmen. Klassendestruktoren bieten dieselben Vorteile wie Klassenkonstruktoren, nur dass sie die Finalisierung vornehmen.
Das folgende Beispiel baut auf dem Beispiel für Klassenkonstruktoren auf und führt die Finalisierungsroutine ein:
type
TBox = class
private
class var FList: TList<Integer>;
class constructor Create;
class destructor Destroy;
end;
implementation
class constructor TBox.Create;
begin
{ Initialize the static FList member }
FList := TList<Integer>.Create();
end;
class destructor TBox.Destroy;
begin
{ Finalize the static FList member }
FList.Free;
end;
end.
Hinweis: Der Klassendestruktor für eine generische Klasse oder einen Record kann mehrfach ausgeführt werden. Die genaue Anzahl der Ausführungen des Klassendestruktors hängt in diesem Fall von der Anzahl der spezialisierten Versionen des generischen Typs ab. Der Klassendestruktor für eine spezialisierte TList<String>-Klasse kann beispielsweise mehrfach in derselben Anwendung ausgeführt werden.
Botschaftsmethoden
In Botschaftsmethoden können Reaktionen auf dynamisch weitergeleitete Botschaften implementiert werden. Die Syntax für Botschaftsmethoden wird auf allen Plattformen unterstützt. In der VCL werden Botschaftsmethoden verwendet, um auf Windows-Botschaften zu antworten.
Sie erstellen eine Botschaftsmethode, indem Sie die Direktive message gefolgt von einer Integer-Konstante von 1 bis 49151 (der sogenannten Botschafts-ID) in eine Methodendeklaration aufnehmen. In Botschaftsmethoden für VCL-Steuerelemente kann als Integer-Konstante eine der Botschafts-IDs von Win32 verwendet werden, die (zusammen mit den entsprechenden Record-Typen) in der Unit Messages definiert sind. Eine Botschaftsmethode muss eine Prozedur mit einem einzelnen var-Parameter sein.
Zum Beispiel:
type
TTextBox = class(TCustomControl)
private
procedure WMChar(var Message: TWMChar); message WM_CHAR;
...
end;
In einer Botschaftsmethode muss die Direktive override nicht angegeben werden, um eine geerbte Botschaftsmethode zu überschreiben. Es muss nicht einmal derselbe Methodenname oder Parametertyp wie bei der zu überschreibenden Methode verwendet werden. Allein die Botschafts-ID bestimmt, auf welche Botschaft die Methode reagiert, und ob die Methode überschrieben wird.
Botschaftsmethoden implementieren
In der Implementierung einer Botschaftsmethode kann die geerbte Botschaftsmethode wie im folgenden Beispiel aufgerufen werden:
procedure TTextBox.WMChar(var Message: TWMChar);
begin
if Message.CharCode = Ord(#13) then
ProcessEnter
else
inherited;
end;
Die Anweisung inherited durchsucht die Klassenhierarchie nach oben und ruft die erste Botschaftsmethode mit derselben ID wie die aktuelle Methode auf. Dabei wird automatisch der Botschafts-Record übergeben. Ist in keiner Vorfahrklasse eine Botschaftsmethode mit dieser ID implementiert, ruft inherited die ursprünglich in TObject definierte Methode DefaultHandler auf.
Die Implementierung von DefaultHandler in TObject gibt einfach die Steuerung zurück, ohne eine Aktion auszuführen. Durch Überschreiben von DefaultHandler kann in einer Klasse eine eigene Standardbehandlung für Botschaften implementiert werden. In Win32 ruft die DefaultHandler-Methode für Steuerelemente die DefWindowProc-Funktion der Win32-API auf.
Botschaftsweiterleitung
Botschaftsmethoden werden normalerweise nicht direkt aufgerufen. Stattdessen werden Botschaften mithilfe der von TObject geerbten Methode Dispatch an ein Objekt weitergeleitet:
procedure Dispatch(var Message);
Der an Dispatch übergebene Parameter Message muss ein Record sein, dessen erstes Element ein Word-Feld mit einer Botschafts-ID enthält.
Dispatch durchsucht die Klassenhierarchie nach oben (beginnend bei der Klasse des Objekts, in dem sie aufgerufen wird) und ruft die erste für die übergebene ID gefundene Botschaftsmethode auf. Wird keine solche Methode gefunden, ruft Dispatch die Methode DefaultHandler auf.