Every
property has a read specifier, a write specifier, or both. These
are called access specifiers and they have the form
read fieldOrMethod
write fieldOrMethod
where fieldOrMethod is the name of a field or method declared in the same class as the property or in an ancestor class.
|
· |
If fieldOrMethod is declared in the same class, it must occur before the property declaration. If it is declared in an ancestor class, it must be visible from the descendant; that is, it cannot be a private field or method of an ancestor class declared in a different unit. |
|
· |
If fieldOrMethod is a field, it must be of the same type as the property. |
|
· |
If fieldOrMethod is a method, it cannot be overloaded. Moreover, access methods for a published property must use the default register calling convention. |
|
· |
In a read specifier, if fieldOrMethod is a method, it must be a parameterless function whose result type is the same as the property's type. |
|
· |
In a write specifier, if fieldOrMethod is a method, it must be a procedure that takes a single value or const parameter of the same type as the property. |
For
example, given the declaration
property
Color: TColor read GetColor write SetColor;
the GetColor method must be declared
as
function
GetColor: TColor;
and the SetColor method must be
declared as one of these:
procedure
SetColor(Value: TColor);
procedure SetColor(const Value: TColor);
(The name of SetColor’s parameter, of course, doesn’t have to be Value.)
When a
property is referenced in an expression, its value is read using the field or
method listed in the read specifier. When a property is referenced in an
assignment statement, its value is written using the field or method listed in
the write specifier.
The
example below declares a class called TCompass with a published property
called Heading. The value of Heading is read through the FHeading
field and written through the SetHeading procedure.
type
THeading = 0..359;
TCompass = class(TControl)
private
FHeading: THeading;
procedure
SetHeading(Value: THeading);
published
property Heading:
THeading read FHeading write SetHeading;
...
end;
Given
this declaration, the statements
if
Compass.Heading = 180 then GoingSouth;
Compass.Heading := 135;
correspond to
if
Compass.FHeading = 180 then GoingSouth;
Compass.SetHeading(135);
In the TCompass
class, no action is associated with reading the Heading property; the read
operation consists of retrieving the value stored in the FHeading field.
On the other hand, assigning a value to the Heading property translates
into a call to the SetHeading method, which, presumably, stores the new
value in the FHeading field as well as performing other actions. For
example, SetHeading might be implemented like this:
procedure
TCompass.SetHeading(Value: THeading);
begin
if FHeading <> Value then
begin
FHeading := Value;
Repaint; // update user interface to reflect new
value
end;
end;
A property whose declaration includes only a read specifier is a read-only property, and one whose declaration includes only a write specifier is a write-only property. It is an error to assign a value to a read-only property or use a write-only property in an expression.
每个属性都有一个read说明符、一个write说明符或者两个都有。它们就是被调用的访问说明符(access specifiers)。访问说明符具有如下形式
read fieldOrMethod
write fieldOrMethod
这里的fieldOrMethod是一个域或方法的名称(不管是域还是方法,都必需是与属性在同一个类或祖先类中声明的)。
|
· |
如果fieldOrMethod在同一个类中声明过,那么它必需出现在属性声明之前。如果它声明于祖先类中,那么它必需在后裔类中是可见的;也就是说,如果祖先类的声明在不同的单元中,那么这里的fieldOrMethod不能是一个私有的域或方法。 |
|
· |
如果fieldOrMethod是一个域,那么它必需与属性具有相同的类型。 |
|
· |
如果fieldOrMethod是一个方法,那么呀不能被重载。而且,公布属性的访问方法必需使用缺省的register调用约定。 |
|
· |
在read说明符中,如果fieldOrMethod是一个方法,那么它必需是一个无参数的函数并且函数返回的类型与属性的类型相同。 |
|
· |
在write说明符中,如果fieldOrMethod是一个方法,那么它必需是一个过程,并且该过程接受一个单独的值参数或const参数,并且参数类型与属性类型相同。 |
例如,给出如下声明
property
Color: TColor read GetColor write SetColor;
这里的GetColor方法必需声明为:
function
GetColor: TColor;
而SetColor方法必需声明为下列两者之一:
procedure
SetColor(Value: TColor);
procedure SetColor(const Value: TColor);
(SetColor的参数名称当然不必是Value。)
当属性在表达式中被引用时,属性的值通过列于read说明符中的域或方法读出。当属性在赋值语句中被引用时(属性位于赋值符号的左边),属性的值通过列于write说明符中的域或方法被写入。
下面的例子声明了一个叫做TCompass的类,该类公布了一个叫做Heading的属性。Heading的值通过FHeading读取并且通过SetHeading过程被写入。
type
THeading = 0..359;
TCompass = class(TControl)
private
FHeading: THeading;
procedure
SetHeading(Value: THeading);
published
property Heading:
THeading read FHeading write SetHeading;
...
end;
给出上面的声明,下列语句
if
Compass.Heading = 180 then GoingSouth;
Compass.Heading := 135;
实际上相应为
if
Compass.FHeading = 180 then GoingSouth;
Compass.SetHeading(135);
在TCompass类中,没有任何动作与Heading属性的读取相关联;read操作的组成只有一部分,即获取存储在FHeading域中的值。另一方面,对Heading属性的赋值解释成为对SetHeading方法的调用,大概推测,该方法会和其他多数方法类似,把新的值存储到FHeading域中。例如,SetHeading方法可能如下实现:
procedure
TCompass.SetHeading(Value: THeading);
begin
if FHeading <> Value then
begin
FHeading := Value;
Repaint; //更新用户界面以反映新值
end;
end;
属性声明中只包括read说明符的属性是只读(read-only)属性;属性声明中只包括write说明符的属性是只写(write-only)属性。向只读属性赋值或者读取只写属性的值都将出错。