Eigenschaften (Object Pascal)

Aus Appmethod Topics
Wechseln zu: Navigation, Suche

Nach oben zu Klassen und Objekte - Index


Dieses Thema enthält Informationen zu folgenden Bereichen:

  • Auf Eigenschaften zugreifen
  • Array-Eigenschaften
  • Indexbezeichner
  • Speicherbezeichner
  • Eigenschaften überschreiben und neu deklarieren
  • Klasseneigenschaften

Allgemeines zu Eigenschaften

Eine Eigenschaft definiert wie ein Feld ein Objektattribut. Felder sind jedoch nur Speicherbereiche, die überprüft und geändert werden können, während Eigenschaften mithilfe bestimmter Aktionen gelesen und geschrieben werden können. Sie erlauben eine größere Kontrolle über den Zugriff auf die Attribute eines Objekts und ermöglichen das Berechnen von Attributen.

Die Deklaration einer Eigenschaft muss einen Namen, einen Typ und mindestens eine Zugriffsangabe enthalten. Die Syntax lautet folgendermaßen:

property Eigenschaftsname[Indizes]: Typ index Integer-Konstante Bezeichner;

wobei

  • Eigenschaftsname ein beliebiger gültiger Bezeichner ist.
  • [Indizes] ist optional und besteht aus einer Folge von durch Strichpunkte getrennten Parameterdeklarationen. Jede Deklaration hat die Form Bezeichner1, ..., Bezeichnern: Typ. Weitere Informationen finden Sie im Abschnitt "Array-Eigenschaften" weiter unten.
  • Typ muss ein vordefinierter oder vorher deklarierter Typbezeichner sein. Eigenschaftsdeklarationen der Form property Num: 0..9 sind somit unzulässig.
  • Die Indexklausel Integer-Konstante ist optional. Weitere Informationen hierzu finden Sie im Abschnitt "Indexbezeichner" weiter unten.
  • Bezeichner besteht aus den Bezeichnern read, write, stored, default (oder nodefault) und implements. Jede Eigenschaftsdeklaration muss zumindest einen read- oder write-Bezeichner enthalten.

Eigenschaften werden durch ihre Zugriffsbezeichner definiert. Sie können im Gegensatz zu Feldern nicht als var-Parameter übergeben oder mit dem Adressoperator @ versehen werden. Der Grund dafür liegt darin, dass eine Eigenschaft nicht notwendigerweise im Speicher vorhanden sein muss. Sie kann beispielsweise eine read-Methode haben, die einen Wert aus einer Datenbank abruft oder einen Zufallswert generiert.

Auf Eigenschaften zugreifen

Jede Eigenschaft verfügt über eine read- oder eine write-Angabe (oder über beide). Diese Zugriffsbezeichner werden in folgender Form angegeben:

read FeldOderMethode
write FeldOderMethode

FeldOderMethode ist der Name eines Feldes oder einer Methode, das bzw. die in derselben Klasse oder in einer Vorfahrklasse der Eigenschaft deklariert ist.

  • Wenn FeldOderMethode in derselben Klasse deklariert wird, muss dies vor der Eigenschaft geschehen. Ist das Element in einer Vorfahrklasse deklariert, muss es in der abgeleiteten Klasse sichtbar sein. Es kann also kein Element einer abgeleiteten Klasse angegeben werden, das in einer anderen Unit als private deklariert ist.
  • Handelt es sich bei FeldOderMethode um ein Feld, muss dieses Feld denselben Typ wie die Eigenschaft haben.
  • Wenn FeldOderMethode eine Methode ist, kann diese nicht dynamic sein. Falls es sich um eine virtual Methode handelt, kann diese nicht überladen werden. Zugriffsmethoden für eine als published deklarierte Eigenschaft müssen die Standard-Aufrufkonvention register verwenden.
  • Wird in einem read-Bezeichner für FeldOderMethode eine Methode angegeben, muss eine Funktion ohne Parameter verwendet werden, die einen Wert mit dem Typ der Eigenschaft zurückgibt. Eine Ausnahme bildet die Zugriffsmethode für eine indizierte Eigenschaft oder eine Array-Eigenschaft.
  • Wird in einem write-Bezeichner für FeldOderMethode eine Methode angegeben, muss eine Prozedur verwendet werden, die einen Wert- oder const-Parameter mit dem Datentyp der Eigenschaft entgegennimmt (oder wenn es sich um eine Array-Eigenschaft oder indizierte Eigenschaft handelt).

Betrachten Sie beispielsweise die folgende Deklaration:

property Color: TColor read GetColor write SetColor;

Die Methode GetColor muss hier folgendermaßen deklariert werden:

function GetColor: TColor;

Die Deklaration der Methode SetColor muss eine der folgenden Formen haben:

procedure SetColor(Value: TColor);
procedure SetColor(const Value: TColor);

(Der Parameter von SetColor muss natürlich nicht unbedingt Value heißen.)

Wenn eine Eigenschaft in einem Ausdruck verwendet wird, erfolgt der Zugriff auf ihren Wert mithilfe des mit read angegebenen Elements (Feld oder Methode). Bei Zuweisungen wird das mit write angegebene Element für das Schreiben der Eigenschaft verwendet.

Im folgenden Beispiel wird die Klasse TCompass mit der published-Eigenschaft Heading deklariert. Zum Lesen der Eigenschaft wird das Feld FHeading, zum Schreiben die Prozedur SetHeading verwendet.

type
   THeading = 0..359;
   TCompass = class(TControl)
     private
        FHeading: THeading;
        procedure SetHeading(Value: THeading);
     published
        property Heading: THeading read FHeading write SetHeading;
        ...
    end;

Ausgehend von dieser Deklaration ist die Anweisung

if Compass.Heading = 180 then GoingSouth;
Compass.Heading := 135;

mit den folgenden Anweisungen identisch:

if Compass.FHeading = 180 then GoingSouth;
Compass.SetHeading(135);

In der Klasse TCompass wird zum Lesen der Eigenschaft Heading keine bestimmte Aktion verwendet. Die read-Operation besteht lediglich aus dem Abrufen des im Feld FHeading gespeicherten Wertes. Wertzuweisungen an die Eigenschaft werden in Aufrufe der Methode SetHeading umgesetzt. Diese Methode speichert den neuen Wert im Feld FHeading und führt optional weitere Operationen durch. SetHeading könnte beispielsweise folgendermaßen implementiert werden:

procedure TCompass.SetHeading(Value: THeading);
begin
            if FHeading <> Value then
             begin
               FHeading := Value;
               Repaint;    // Aktualisieren der Benutzeroberfläche, um den neuen Wert anzuzeigen
             end;
end;

Eine Eigenschaft, die nur mit einer read-Angabe deklariert ist, nennt man Nur-Lesen-Eigenschaft (schreibgeschützt). Ist nur ein write-Bezeichner vorhanden, handelt es sich um eine Nur-Schreiben-Eigenschaft (lesegeschützt). Wenn Sie einer Nur-Lesen-Eigenschaft einen Wert zuweisen oder eine Nur-Schreiben-Eigenschaft in einem Ausdruck verwenden, tritt ein Fehler auf.

Array-Eigenschaften

Array-Eigenschaften sind indizierte Eigenschaften. Sie werden beispielsweise für die Einträge eines Listenfeldes, die untergeordneten Objekte eines Steuerelements oder die Pixel einer Bitmap-Grafik verwendet.

Die Deklaration einer Array-Eigenschaft enthält eine Parameterliste mit den Namen und Typen der Indizes. Beispiel:

property Objects[Index: Integer]: TObject read GetObject write SetObject;
property Pixels[X, Y: Integer]: TColor read GetPixel write SetPixel;
property Values[const Name: string]: string read GetValue write SetValue;

Das Format der Liste ist mit dem Format der Parameterliste einer Prozedur oder Funktion identisch. Der einzige Unterschied besteht darin, dass die Parameterdeklarationen nicht in runden, sondern in eckigen Klammern angegeben werden. Im Gegensatz zu Arrays, bei denen nur ordinale Indizes erlaubt sind, können die Indizes von Array-Eigenschaften einen beliebigen Typ haben.

Bei Array-Eigenschaften müssen die Zugriffsbezeichner keine Felder, sondern Methoden angeben. Die Methode in einem read-Bezeichner muss eine Funktion sein, bei der Anzahl, Reihenfolge und Typ der Parameter mit der Indexparameterliste der Eigenschaft identisch sind, und der Ergebnistyp mit dem Typ der Eigenschaft übereinstimmt. Die Methode in einem write-Bezeichner muss eine Prozedur sein, bei der Anzahl, Reihenfolge und Typ der Parameter mit der Indexparameterliste der Eigenschaft identisch sind. Außerdem muss ein zusätzlicher Wert- oder const-Parameter mit dem Typ der Eigenschaft vorhanden sein.

Die Zugriffsmethoden für die obigen Array-Eigenschaften können beispielsweise folgendermaßen deklariert werden:

function GetObject(Index: Integer): TObject;
function GetPixel(X, Y: Integer): TColor;
function GetValue(const Name: string): string;
procedure SetObject(Index: Integer; Value: TObject);
procedure SetPixel(X, Y: Integer; Value: TColor);
procedure SetValue(const Name, Value: string);

Auf eine Array-Eigenschaft kann durch Indizieren ihres Bezeichners zugegriffen werden. So sind beispielsweise die Anweisungen

if Collection.Objects[0] = nil then Exit;
Canvas.Pixels[10, 20] := clRed;
Params.Values['PATH'] := 'C:\BIN';

mit den folgenden Anweisungen identisch:

if Collection.GetObject(0) = nil then Exit;
Canvas.SetPixel(10, 20, clRed);
Params.SetValue('PATH', 'C:\BIN');

Wenn Sie die Direktive default nach der Definition einer Array-Eigenschaft angeben, wird diese als Standardeigenschaft der betreffenden Klasse verwendet. Beispiel:

type
   TStringArray = class
    public
       property Strings[Index: Integer]: string ...; default;
          ...
    end;

Auf die Array-Standardeigenschaft einer Klasse kann mit der Kurzform Objekt[Index] zugegriffen werden. Diese Anweisung ist mit Objekt.Eigenschaft[Index] identisch. Ausgehend von der vorhergehenden Deklaration kann z.B. StringArray.Strings[7] zu StringArray[7] verkürzt werden. Eine Klasse kann nur eine Standardeigenschaft mit der gegebenen Signatur (Array-Parameterliste) haben, aber es ist möglich, die Standardeigenschaft zu überladen. Das Ändern oder Verbergen der Standardeigenschaft in abgeleiteten Klassen kann zu unerwünschten Ergebnissen führen, da der Compiler Bindungen an Eigenschaften immer statisch vornimmt.

Indexbezeichner

Mithilfe von Indexbezeichnern können mehrere Eigenschaften dieselbe Zugriffsmethode verwenden, auch wenn sie unterschiedliche Werte repräsentieren. Indexbezeichner bestehen aus der Direktive index und einem Integer-Wert zwischen -2.147.483.647 und 2.147.483.647. Bei Eigenschaften mit Indexbezeichnern muss auf die Direktiven read und write eine Methode (kein Feld) folgen. Beispiel:

type
   TRectangle = class
     private
       FCoordinates: array[0..3] of Longint;
       function GetCoordinate(Index: Integer): Longint;
       procedure SetCoordinate(Index: Integer; Value: Longint);
     public
       property Left: Longint index 0  read GetCoordinate 
                                       write SetCoordinate;
       property Top: Longint index 1   read GetCoordinate 
                                       write SetCoordinate;
       property Right: Longint index 2 read GetCoordinate
                                       write SetCoordinate;
       property Bottom: Longint index 3 read GetCoordinate
                                        write SetCoordinate;
       property Coordinates[Index: Integer]: Longint 
                                       read GetCoordinate
                                       write SetCoordinate;
       ...
   end;

Eine Zugriffsmethode für eine Eigenschaft mit einem Indexbezeichner benötigt einen zusätzlichen Wert-Parameter vom Typ Integer. Bei einer read-Funktion muss dies der letzte, bei einer write-Prozedur der vorletzte Parameter sein. Diese Konstante (der Index) wird beim Zugriff auf die Eigenschaft automatisch an die Zugriffsmethode übergeben.

Wenn Rectangle ein Objekt der zuvor deklarierten Klasse TRectangle ist, dann ist die Anweisung

Rectangle.Right := Rectangle.Left + 100;

mit der folgenden Anweisung identisch:

Rectangle.SetCoordinate(2, Rectangle.GetCoordinate(0) + 100);

Speicherbezeichner

Die optionalen Direktiven stored, default und nodefault sind Speicherbezeichner. Sie haben keinerlei Auswirkungen auf die Funktionsweise des Programms, sondern bestimmen, ob die Werte der published-Eigenschaften in Formulardateien gespeichert werden.

Nach der Angabe stored muss der Wert True oder False, der Name eines booleschen Feldes oder der Name einer parameterlosen Methode folgen, die einen booleschen Wert zurückgibt. Beispiel:

property Name : TComponentName read FName write SetName stored False;

Wird eine Eigenschaft ohne die Angabe von stored deklariert, entspricht dies der Definition stored True.

Nach default muss eine Konstante angegeben werden, die denselben Datentyp wie die Eigenschaft hat. Beispiel:

property Tag: Longint read FTag write FTag default 0;

Mithilfe des Bezeichners nodefault kann ein geerbter default-Wert ohne Angabe eines neues Wertes außer Kraft gesetzt werden. Die Direktiven default und nodefault werden nur für Ordinal- und Mengentypen unterstützt, bei denen die Ober- und Untergrenze des Basistyps einen Ordinalwert zwischen 0 und 31 hat. Enthält eine Eigenschaftsdeklaration weder default noch nodefault, gilt sie als mit nodefault definiert. Für Real-, Zeiger- und String-Typen gilt der implizite default-Wert 0 bzw. nil und '' (leerer String).

Anmerkung: Sie können den ordinalen Wert 2147483648 nicht als Standardwert verwenden. Dieser Wert wird intern für die Darstellung von nodefault verwendet.

Beim Speichern einer Komponente werden die Speicherbezeichner ihrer published-Eigenschaften überprüft. Wenn sich der aktuelle Wert einer Eigenschaft von ihrem default-Wert unterscheidet (oder kein default-Wert vorhanden ist) und stored True ist, wird der Wert gespeichert. Treffen diese Bedingungen nicht zu, wird der Wert nicht gespeichert.

Anmerkung: Eigenschaftswerte werden nicht automatisch auf den Standardwert initialisiert, d.h. die Standarddirektive steuert nur das Speichern von Eigenschaftswerten in der Formulardatei und nicht das Speichern des ersten Werts der Eigenschaft in einer neu erstellten Instanz.

Bei Array-Eigenschaften werden Speicherbezeichner nicht unterstützt. Bei diesem Eigenschaftstyp hat default eine andere Bedeutung (siehe "Array-Eigenschaften" weiter oben).

Eigenschaften überschreiben und neu deklarieren

Das Deklarieren einer Eigenschaft ohne Angabe eines Typs nennt man Überschreiben. Diese Vorgehensweise ermöglicht das Ändern der geerbten Sichtbarkeit bzw. des geerbten Bezeichners einer Eigenschaft. In der einfachsten Form braucht nur das reservierte Wort property zusammen mit einem geerbten Eigenschaftsbezeichner angegeben zu werden. Auf diese Weise kann die Sichtbarkeit der betreffenden Eigenschaft geändert werden. So kann beispielsweise eine in einer Vorfahrklasse als protected deklarierte Eigenschaft im public- oder published-Abschnitt einer abgeleiteten Klasse neu deklariert werden. Eigenschaftsüberschreibungen können die Angaben read, write,stored, default und nodefault enthalten, durch die die entsprechende geerbte Direktive außer Kraft gesetzt wird. Mithilfe einer Überschreibung können Sie geerbte Zugriffsangaben ersetzen, fehlende Angaben hinzufügen oder den Gültigkeitsbereich einer Eigenschaft erweitern, jedoch keine Zugriffsbezeichner entfernen oder die Sichtbarkeit verringern. Optional kann auch mit der Direktive implements die Liste der implementierten Interfaces ergänzt werden, ohne die geerbten Interfaces zu entfernen.

Die folgenden Deklarationen zeigen, wie Eigenschaften überschrieben werden können:

type
   TAncestor = class
       ...
     protected
       property Size: Integer read FSize;
       property Text: string read GetText write SetText;
       property Color: TColor read FColor write SetColor stored False;
       ...
   end;

type

   TDerived = class(TAncestor)
       ...
     protected
       property Size write SetSize;
     published
       property Text;
       property Color stored True default clBlue;
       ...
   end;

Beim Überschreiben von Size wird die Angabe write hinzugefügt, damit der Wert der Eigenschaft geändert werden kann. Die Sichtbarkeit der Eigenschaften Text und Color wird von protected in published geändert. Für die Eigenschaft Color wird außerdem festgelegt, dass sie in der Formulardatei gespeichert wird, wenn sie einen anderen Wert als clBlue hat.

Wenn Sie beim Redeklarieren einer Eigenschaft einen Typbezeichner angeben, wird die geerbte Eigenschaft nicht überschrieben, sondern lediglich verdeckt. Dadurch wird eine neue Eigenschaft mit demselben Namen wie die geerbte erstellt. Diese Art der Deklaration muss immer vollständig vorgenommen werden. Stets muss zumindest eine Zugriffsangabe vorhanden sein.

Unabhängig davon, ob eine Eigenschaft in einer abgeleiteten Klasse verdeckt oder überschrieben wird, erfolgt die Suche nach der Eigenschaft immer statisch. Der deklarierte (also zur Compilierzeit bekannte) Typ der Variable bestimmt die Interpretation des Eigenschaftsbezeichners. Aus diesem Grund wird nach dem Ausführen des folgenden Codes durch das Lesen oder Schreiben von MyObject.Value die Methode Method1 bzw. Method2 aufgerufen, obwohl MyObject eine Instanz von TDescendant enthält. Sie können aber durch eine Typumwandlung in TDescendant auf die Eigenschaften und Zugriffsangaben der abgeleiteten Klasse zugreifen.

type
    TAncestor = class
      ...
      property Value: Integer read Method1 write Method2;
    end;

    TDescendant = class(TAncestor)
      ...
      property Value: Integer read Method3 write Method4;
    end;

 var MyObject: TAncestor;
      ...
     MyObject := TDescendant.Create;

Klasseneigenschaften

Auf Klasseneigenschaften kann ohne Objektreferenz zugegriffen werden. Methoden für den Zugriff auf Klasseneigenschaften müssen als klassenstatisch bzw. als Klassenfelder deklariert sein. Eine Klasseneigenschaft wird mit dem Schlüsselwörtern class property deklariert. Klasseneigenschaften können nicht als published deklariert werden und keine stored- oder default-Wertdefinitionen haben.

Mithilfe einer class var-Deklaration können Sie einen Block von statischen Klassenfeldern innerhalb einer Klassendeklaration erzeugen. Alle nach class var deklarierten Felder haben statische Speicherattribute. Ein class var-Block wird durch Folgendes abgeschlossen:

  1. Eine weitere class var-Deklaration
  2. Eine Prozedur- oder Funktionsdeklaration (Methodendeklaration) (einschließlich Klassenprozeduren und Klassenfunktionen)
  3. Eine Eigenschaftsdeklaration (einschließlich Klasseneigenschaften)
  4. Eine Konstruktor- oder Destruktor-Deklaration
  5. Ein Sichtbarkeitsattribut (public, private, protected, published, strict private und strict protected)

Beispiel:

type
   TMyClass = class
     strict private
       class var         // Felder müssen als Klassenfelder deklariert werden
          FRed: Integer;
          FGreen: Integer;
          FBlue: Integer;
       public             // Ende des class var-Blocks
          class property Red: Integer read FRed write FRed;
          class property Green: Integer read FGreen write FGreen;
          class property Blue: Integer read FBlue write FBlue;
   end;

Auf die obigen Klasseneigenschaften kann folgendermaßen zugegriffen werden:

TMyClass.Red := 0;
TMyClass.Blue := 0;
TMyClass.Green := 0;

Siehe auch