A
property declaration that doesn’t specify a type is called a property override.
Property overrides allow you to change a property’s inherited visibility or
specifiers. The simplest override consists only of the reserved word property
followed by an inherited property identifier; this form is used to change a
property’s visibility. For example, if an ancestor class declares a property as
protected, a derived class can redeclare it in a public or published section of
the class. Property overrides can include read, write, stored,
default, and nodefault directives; any such directive overrides
the corresponding inherited directive. An override can replace an inherited
access specifier, add a missing specifier, or increase a property’s visibility,
but it cannot remove an access specifier or decrease a property’s visibility.
An override can include an implements directive, which adds to the list
of implemented interfaces without removing inherited ones.
The
following declarations illustrate the use of property overrides.
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;
The
override of Size adds a write specifier to allow the property to
be modified. The overrides of Text and Color change the
visibility of the properties from protected to published. The property override
of Color also specifies that the property should be filed if its value
isn’t clBlue.
A
redeclaration of a property that includes a type identifier hides the inherited
property rather than overriding it. This means that a new property is created
with the same name as the inherited one. Any property declaration that
specifies a type must be a complete declaration, and must therefore include at
least one access specifier.
Whether a
property is hidden or overridden in a derived class, property look-up is always
static. That is, the declared (compile-time) type of the variable used
to identify an object determines the interpretation of its property
identifiers. Hence, after the following code executes, reading or assigning a
value to MyObject.Value invokes Method1 or Method2,
even though MyObject holds an instance of TDescendant. But you
can cast MyObject to TDescendant to access the descendant class’s
properties and their access specifiers.
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;
Implementing
interfaces by delegation
不指定属性类型的属性声明叫做属性覆盖(property override)。属性覆盖允许改变属性的继承的可见度或者说明符。最简单的覆盖,只使用保留字property和紧随其后的继承得到的属性标识符;这种形式用于改变属性的可见度。例如,如果一个祖先类声明了一个保护属性,那么派生类可以在公共或公布部分对其进行再声明。属性覆盖可以包括read、write、stored、default和nodefault等指示字;任何这样的指示字都覆盖相应继承得到的指示字。属性覆盖可以替换继承的访问说明符,增加丢失的说明符,或者提高属性的可见度,但却不能删除访问说明符或降低属性的可见度。属性覆盖可以包括implements指示字,这样只是增加属性到实现的接口列表而没有删除继承得到的属性。
下面的声明举例说明了属性覆盖的使用。
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;
Size的覆盖增加了write说明符以允许属性被修改。Text和Color的覆盖改变了属性的可见度:从保护属性提高为公布属性。此外,Color的属性覆盖还增加了default说明符,使该域在值不是clBlue时保存到文件。
包括了类型标识符的属性再声明将隐藏继承得到的属性,这优于属性覆盖。也就是说,创建了一个与继承的属性同名的新的属性。任何指定了类型的属性声明必需是一个完整的声明,并且因此必需至少包括一个访问说明符。
不管属性在派生类中是被隐藏还是被覆盖,属性查找总是静态的。也就是说,用于标识对象的变量,其声明时(编译时)的类型决定了其属性标识符的解释。因此,下列代码执行后,对MyObject.Value的读或写将调用Method1或Method2,尽管MyObject保存的是TDescendant实例。不过,可以将MyObject强制转换为TDescendant类型,以访问后裔类的属性及相应的访问说明符。
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;