Properties

Properties provide abstraction of a types data by combining the concepts of Fields (to store data in a Class or Record) and Methods (to perform actions on that data) into a single entity.

When accessed, a property behaves much like a field – it has a value that can be read and (optionally) updated. But unlike fields, accessing a property does not directly give unfettered access to the stored data in the class or record. Instead, access to a property goes through custom method-likegetter and setter code.

This provides three main benefits:

Properties can be read/write or read-only (and in rare cases even write-only)

Combined, these aspects allow classes (and records) to take control of the data by not allowing outside access to their fields, which any external code could modify in an uncontrolled manner.

In fact, it is considered good practise to have all fields of a class marked private, so only the class's code itself can access them, and funnel all external modifications through properties (or regular Methods, of course).

A forth benefit of properties is that their getters can generste or modify the returned value dynamically – so not every property necessarily maps directly to a value stored in a field.

Property Declaration Syntax

A simple property declaration consists of the property keyword, followed by a property name and a (result) type, separated with a colon and optional getter (read) and setter (write) statements:

property method Name: String read fName write SetNameAndUpdateView;

If only a getter or or only a setter is provided, the property will be read-only or write-only, respectively. If neither getter or setter is provided, the compiler will automatically provide a field for storage, and a simple getter and setter that uses that field. Such a property works much like a regular Field, then, from a usage level.

The getter can be any Expression that returns the correct type. This could be a simple field access (as in the example above), a method, or a more complex expression:

Alternatively, a full begin/end block of statements can also be provided for either the getter or the setter. In this case, the value Expression can be used to access the incoming value, within the setter code:

Stored Properties

As mentioned above, if neither a getter or setter are provided, the property will be read/write, and the compiler will automatically generate getters and setters that store and obtain the value from a (hidden) backing variable. In this case, the property behaves very much like a plain field:

property Name: String; // internally stored in a hidden String var

Different that an actual field, stored properties still are exposed via getter and setters, so thye can be "upgraded" to use custom getters or setter later, without breaking binary compatibility of a type. Also, they still will support the notify Modifier, and other property-specific features.

Stored properties can be marked with the readonlyMember Modifier to become read-only. Read-only properties can still be written to from an Initializer or from the class's Constructors – but they cannot be modified once construction of an instance has completed.

Initializers

Like Fields, Stored Properties can be assigned an initial value right in their declaration by having the property declaration closed off with the := operator followed by an expression. Optionally, they can be marked with the lazyMember Modifier to defer execution of the initializer until the first time the property is accessed.

Indexer Properties

While regular properties represent a single value (of an arbitrary type, of course), indexer properties can provide access to a range of values of the same type. This is similar in concept to an Array, but each access – read or write – goes through the proper getter or setter code.

An indexer property is declared by providing one or more parameters after the property name, enclosed in square brackets. Indexer properties cannot be stored properties, so either a read or a write statement is required.

property Items[aIndex: Integer]: String read ... write ...;

The rules for read and write statements are similar to regular properties: The name of the indexer parameter(s) may be used in the statements, and if the name of a getter of setter method is provided, the signature of this method must include parameters that matycxh the property's parameters:

Of course, an indexer property does not necessarily have to be backed to an array-like structurel it can also generate (or store) values more dynamically. In the example below, the IntsAsStrings could be accessed with any arbitrary index, and would return the approriate string.

Indexer properties can have more than one parameter (i.e. be multi-dimensional), and – different that Arrays – they can be indexed on any arbitrate type, ot just Integers.

Note that, also unlike arrays, indexer properties themselves have no concept of a count, or a valid range of parameters. It is up to the type implementing the property to provide clear semantics as to how an indexer can be accessed. For example, a List class indexed with integer indices might expose a separate Count property, while a dictionary would allow arbitrary indexes – and might decide to raise an exception, or return nil for values not in the dictionary.

Default Indexers

One indexer property per class (optionally overloaded on type) can be marked with the defaultMember Modifier. The default property can then be accessed by omitting the property name accessing the indexer off an instance (or type) itself;

Property Notifications

Non-indexed properties can optionally be markled with the notifyMember Modifier. Properties marked with notify will emit special platform-specific events whenever they are changed – allowing other parts of code to be notified about and react to these changes.

How these notifications work and how they can be retrieved depends ion the underlying platform. Notifications are used heavily in WPF on .NET or with Key-Value-Observation (KVO) on Cocoa.

Please refer to the Property Notifications topic for more details on this.

Storage Modifiers (Cocoa)

On the Cocoa platform, the type of a stored property declaration can be amended with one of the weak, unretained or strongStorage Modifier keywords, with strong being the implied default.

property Value: weak String;

To specify a Storage Modifier, the type cannot be inferred, but must be explicitly specified. Inferred types will always be considered strong.

Cocoa Only

Static Properties

Like most type members, properties are by default defined on the instance – that means the property can be called on and will execute in the context of an instance of the class. A property can be marked as static by prefixing the property declaration with the class keyword, or by applying the staticMember Modifier:

Visibility

By default, both getter and setter of the property are accessible on that visibility level, but visibility can be overridden by prefixing either the getter or the setter with a separate visibility keyword:

Virtuality

Properties can be marked as abstract, if a descendant class must provide the implementation. Abstract properties (and properties in Interfaces may not define a getter or setter, but they can have optionally specify the read and/or write keywords to indicate whether a property can be read, written or both: