6.4 Properties

Classes can contain properties as part of their elds list. A property acts like a normal eld, i.e. its value can be retrieved or set, but it allows to redirect the access of the eld through functions and procedures. They provide a means to associate an action with an assignment of or a reading from a class 'eld'. This allows for e.g. checking that a value is valid when assigning, or, when reading, it allows to construct the value on the y. Moreover, properties can be read-only or write only. The prototype declaration of a property is as follows:

_________________________________________________________________________________________________________ Properties
-- --property de  nition property -identi  er------------------
- ----             --------------------property interface--------------
      property speci ers

-- --property interface -----------------------:-type identi  er-
- -------------------property parameter list---------------------------
     -     -            --|
      index  integerconstant

-- --property parameter list [--6parameter declaration--] ------------------
                           ---------;---------

-- --property speci  ers-|-------------|--------------|----------------
                     -read speci  er- -write speci  er -default speci  er

-- --read speci  er read -  eld or method-------------------------------

-- --write speci  er-write  eld or method------------------------------

-- --            ---      ------------------------------------------
     default speci  er|default  -constant-| |
                   ------nodefault ------|

-- --            ----           ------------------------------------
       eld or method    - eld identi  er  -|
                    method identi  er
___________________________________________________________________

A read specifier is either the name of a eld that contains the property, or the name of a method function that has the same return type as the property type. In the case of a simple type, this function must not accept an argument. A read specifier is optional, making the property write-only. Note that class methods cannot be used as read speciers. A write specifier is optional: If there is no write specifier, the property is read-only. A write specier is either the name of a eld, or the name of a method procedure that accepts as a sole argument a variable of the same type as the property. The section (private, published) in which the specied function or procedure resides is irrelevant. Usually, however, this will be a protected or private method. Example: Given the following declaration:

Type  
  MyClass = Class  
    Private  
    Field1 : Longint;  
    Field2 : Longint;  
    Field3 : Longint;  
    Procedure  Sety (value : Longint);  
    Function Gety : Longint;  
    Function Getz : Longint;  
    Public  
    Property X : Longint Read Field1 write Field2;  
    Property Y : Longint Read GetY Write Sety;  
    Property Z : Longint Read GetZ;  
    end;  
Var MyClass : TMyClass;

The following are valid statements:

WriteLn ('X : ',MyClass.X);  
WriteLn ('Y : ',MyClass.Y);  
WriteLn ('Z : ',MyClass.Z);  
MyClass.X := 0;  
MyClass.Y := 0;

But the following would generate an error:

MyClass.Z := 0;

because Z is a read-only property. What happens in the above statements is that when a value needs to be read, the compiler inserts a call to the various getNNN methods of the object, and the result of this call is used. When an assignment is made, the compiler passes the value that must be assigned as a paramater to the various setNNN methods. Because of this mechanism, properties cannot be passed as var arguments to a function or procedure, since there is no known address of the property (at least, not always). If the property denition contains an index, then the read and write speciers must be a function and a procedure. Moreover, these functions require an additional parameter : An integer parameter. This allows to read or write several properties with the same function. For this, the properties must have the same type. The following is an example of a property with an index:

{$mode objfpc}  
Type TPoint = Class(TObject)  
       Private  
       FX,FY : Longint;  
       Function GetCoord (Index : Integer): Longint;  
       Procedure SetCoord (Index : Integer; Value : longint);  
       Public  
       Property X : Longint index 1 read GetCoord Write SetCoord;  
       Property Y : Longint index 2 read GetCoord Write SetCoord;  
       Property Coords[Index : Integer]:Longint Read GetCoord;  
       end;  
Procedure TPoint.SetCoord (Index : Integer; Value : Longint);  
begin  
  Case Index of  
   1 : FX := Value;  
   2 : FY := Value;  
  end;  
end;  
Function TPoint.GetCoord (INdex : Integer) : Longint;  
begin  
  Case Index of  
   1 : Result := FX;  
   2 : Result := FY;  
  end;  
end;  
Var P : TPoint;  
begin  
  P := TPoint.create;  
  P.X := 2;  
  P.Y := 3;  
  With P do  
    WriteLn ('X=',X,' Y=',Y);  
end.

When the compiler encounters an assignment to X, then SetCoord is called with as rst parameter the index (1 in the above case) and with as a second parameter the value to be set. Conversely, when reading the value of X, the compiler calls GetCoord and passes it index 1. Indexes can only be integer values. Array propertie also exist. These are properties that accept an index, just as an array does. Only now the index doesn't have to be an ordinal type, but can be any type.

A read specifier for an array property is the name method function that has the same return type as the property type. The function must accept as a sole arguent a variable of the same type as the index type. For an array property, one cannot specify elds as read specifiers.

A write specifier for an array property is the name of a method procedure that accepts two arguments: The rst argument has the same type as the index, and the second argument is a parameter of the same type as the property type. As an example, see the following declaration:

Type TIntList = Class  
      Private  
      Function GetInt (I : Longint) : longint;  
      Function GetAsString (A : String) : String;  
      Procedure SetInt (I : Longint; Value : Longint;);  
      Procedure SetAsString (A : String; Value : String);  
      Public  
      Property Items [i : Longint] : Longint Read GetInt  
                                             Write SetInt;  
      Property StrItems [S : String] : String Read GetAsString  
                                              Write SetAsstring;  
      end;  
Var AIntList : TIntList;

Then the following statements would be valid:

AIntList.Items[26] := 1;  
AIntList.StrItems['twenty-five'] := 'zero';  
WriteLn ('Item 26 : ',AIntList.Items[26]);  
WriteLn ('Item 25 : ',AIntList.StrItems['twenty-five']);

While the following statements would generate errors:

AIntList.Items['twenty-five'] := 1;  
AIntList.StrItems[26] := 'zero';

Because the index types are wrong. Array properties can be declared as default properties. This means that it is not necessary to specify the property name when assigning or reading it. If, in the previous example, the denition of the items property would have been

 Property Items[i : Longint]: Longint Read GetInt  
                                      Write SetInt; Default;

Then the assignment

AIntList.Items[26] := 1;

Would be equivalent to the following abbreviation.

AIntList[26] := 1;

Only one default property per class is allowed, and descendent classes cannot redeclare the default property.