6.7.7 Overriding and redeclaring properties

Properties can be both overridden and redeclared in descendent classes.

Property redeclaration takes action if the property type is declared, otherwise it is property override. The only difference is that property override replaces or extends the inherited modifiers with the new modifiers, whereas property redeclaration hides all inherited modifiers that are not present in the redeclaration. The type of the redeclared property does not have to be the same as the parent”s class property type.

The example below demonstrates the difference between property override and redeclaration.

type
  TAncestor = class
  private
    FP1 : Integer;
  public
    property P: integer Read FP1 write FP1;
  end;

  TP1 = class(TAncestor)
  public
    // property override
    property P default 1;
  end;

  TPReadOnly = class(TAncestor)
  public
    // property redeclaration
    property P: integer Read FP1;
  end;

TP1 extends property P with a default value, TPReadOnly redeclares property P as read-only.

Remark TP1 should set the default value of P to 1 in its constructor.

In case of both property redeclaration and property override, the access to the getter and setter is always static. I.e. property override acts only on the RTTI of the object and is not to be confused with method override.

The keyword “inherited” can be used to refer to the parent definition of the property. For example consider the following code:

type
  TAncestor = class
  private
    FP1 : Integer;
  public
    property P: integer Read FP1 write FP1;
  end;

  TClassA = class(TAncestor)
  private
    procedure SetP(const AValue: char);
    function getP : Char;
  public
    constructor Create;
    property P: char Read GetP write SetP;
  end;

procedure TClassA.SetP(const AValue: char);

begin
  Inherited P:=Ord(AValue);
end;

procedure TClassA.GetP : char;

begin
  Result:=Char((Inherited P) and $FF);
end;

TClassA redefines P as a character property instead of an integer property, but uses the parent”s P property to store the value.

Care must be taken when using virtual get/set routines for a property: setting the inherited property still observes the normal rules of inheritance for methods. Consider the following example:

type
  TAncestor = class
  private
    procedure SetP1(const AValue: integer); virtual;
  public
    property P: integer write SetP1;
  end;

  TClassA = class(TAncestor)
  private
    procedure SetP1(const AValue: integer); override;
    procedure SetP2(const AValue: char);
  public
    constructor Create;
    property P: char write SetP2;
  end;

constructor TClassA.Create;
begin
  inherited P:=3;
end;

In this case, when setting the inherited property P, the implementation TClassA.SetP1 will be called, because the SetP1 method is overridden.

If the parent class implementation of SetP1 must be called, then this must be called explicitly:

constructor TClassA.Create;
begin
  inherited SetP1(3);
end;

The redeclared ancestor properties are also available from inside and outside the descendant object with a direct cast to the ancestor:

function GetP(const AClassA: TClassA): Integer;
begin
  Result := TAncestor(AClassA).P;
end;