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;