Icon Properties

Properties are one of the fundamental ways that Object Pascal provides encapsulation in classes. You can have properties directly reference variables or you can also use methods to control the reading and/or writing of variables. This helps to further hide the implementation details of a class and provide an easy-to-use class interface.

A property is declared as follows:

property <Property Name>: <Type Name> read <Variable Name>|<Method Name>
                                     [write <Variable Name>|<Method Name>]
                                     [default <Default Expression>]
                                     [description <Description>]
                                     [;default];

All properties must specify a variable or method name in the read clause, but the write clause is optional. If the write clause is not specified, then the property is implicitly read-only and cannot be assigned a value.

The following example shows a simple class with two read/write property declarations that directly reference variables:

interface

   TCustomer = class
      private
         FID: Integer;
         FName: String;
      public
         property ID: Integer read FID write FID;
         property Name: String read FName write FName;
      end;

The following example shows the same class, but modified to track modifications to any of the variables:

interface

   TCustomer = class
      private
         FID: Integer;
         FName: String;
         FModified: Boolean;
         procedure SetID(Value: Integer);
         procedure SetName(const Value: String);
      public
         property ID: Integer read FID write SetID;
         property Name: String read FName write SetName;
         property Modified: Boolean read FModified;
      end;

implementation

procedure TCustomer.SetID(Value: Integer);
begin
   if (Value <> FID) then
      begin
      FID:=Value;
      FModified:=True;
      end;
end;

procedure TCustomer.SetName(const Value: String);
begin
   if (not SameStr(Value,FID)) then
      begin
      FName:=Value;
      FModified:=True;
      end;
end;

The default clause specifies the default value for a property, and is optional. This default value is used to determine if the property should be streamed or not. If a default value is not provided for a property, and the property is published, then it will always be streamed when an instance of the owner class is streamed.

The description clause specifies the description for a property, and is also optional. This description appears in the IDE's component inspector when the property is declared in the published scope, or is promoted to the published scope in an ancestor class.

The terminating default clause is different from the default value clause above. It is used to specify default array properties and default event properties. See below for more information on default array properties. A default event property is used by the IDE to determine which event handler should automatically be created when a developer double-clicks on a control on the designer surface.

Array Properties
There is one special type of property that is a fairly powerful construct, and that is the array property. An array property is declared as follows:

property <Property Name>[[const] <Parameter Name>: <Type Name>]: <Type Name>
              read <Array Variable Name>|<Method Name>
              [write <Array Variable Name>|<Method Name>]; [default;]

An array property acts like an array but does not necessarily use an array variable for the read and/or write clauses of the property. Such a property can use methods instead of array variables, with the read clause requiring a method that accepts an identical single parameter that matches the array property parameter declaration and a write clause requiring a method that accepts the same parameter name and type and an additional parameter that can have any name that you wish, but whose type must match the type of the property.

Information If you specify an array variable in the read or write clause, then the array property parameter must be an Integer type to reflect the ordinal index into the array.

For example, the following read-only property declares an array property that uses a method to return a specific class instance from an internal list:

TCustomers = class
   private
      FCustomers: TObjectList;
      function GetCustomer(ID: Integer): TCustomer;
   public
      property Customer[ID: Integer]: TCustomer read GetCustomer;
   end;

You would reference the Customer array property as follows:

var
   TempCustomers: TCustomers;
begin
   TempCustomers:=TCustomers.Create(10);
   try
      ShowMessage(TempCustomers.Customer[2].Name);
   finally
      TempCustomers.Free;
   end;
end;

Array properties make it very easy to hide the internal implementation of lists.

Default Array Properties
The above array property example illustrates a common problem with array properties used with classes that encapsulate lists: they tend to bloat the code by requiring the name of the array property to be specified. This is where default array properties are very useful. A default array property has the same declaration as a normal array property, but includes the default keyword after the declaration. To continue with the above example, let's change it to a default array property:

TCustomers = class
   private
      FCustomers: TObjectList;
      function GetCustomer(ID: Integer): TCustomer;
   public
      property Customer[ID: Integer]: TCustomer read GetCustomer; default;
   end;

You would reference the Customer default array property as follows:

var
   TempCustomers: TCustomers;
begin
   TempCustomers:=TCustomers.Create(10);
   try
      ShowMessage(TempCustomers[2].Name);
   finally
      TempCustomers.Free;
   end;
end;

Notice that since the Customer property is marked as the default array property, you no longer need to specify its name, only the instance variable name of the containing Customers class.

Overloaded Array Properties
One final interesting feature of array properties is that they can be overloaded so that you can have multiple such properties with the same name, but with different declarations. This is especially useful when you want to allow access to a list via different search types. For example, here is the above example with the Customer default array property overloaded with an additional declaration that uses a name parameter for retrieving a customer instead of an ID:

TCustomers = class
   private
      FCustomers: TObjectList;
      function GetCustomer(ID: Integer): TCustomer;
      function GetCustomer(const Name: String): TCustomer;
   public
      property Customer[ID: Integer]: TCustomer read GetCustomer; default;
      property Customer[const Name: String]: TCustomer read GetCustomer; default;
   end;

You could then reference the Customer default array property in both ways:

var
   TempCustomers: TCustomers;
begin
   TempCustomers:=TCustomers.Create(10);
   try
      ShowMessage(TempCustomers[2].Name);
      ShowMessage(IntToStr(TempCustomers['Demo Customer #2'].ID));
   finally
      TempCustomers.Free;
   end;
end;

Class Properties
Class properties are special types of properties that are sometimes referred to as "static" properties. They can be referenced from class instances and also directly from class references where no instance of the class exists, and are useful for implementing properties that should be encapsulated within the context of a class, but don't need to access class instance variables, properties, or methods. Class properties can, however, access any class variables or class methods that are also declared in the same class. Please see the Variables and Methods topics for more information on class variables and methods.

Class properties are declared by prefacing a property declaration with the class keyword. For example, the following class declaration includes a class variable that keeps track of how many instances of the class have been created along with a class property that returns the creation count:

TMyClass = class
   private
      class FCreateCount: Integer;
   public
      constructor Create; override;
      class property CreateCount: Integer read FCreateCount;
   end;

implementation

constructor TMyClass.Create;
begin
   inherited Create;
   Inc(FCreateCount);
end;

The CreateCount class property above could be accessed in two different ways. The first way is by referring to the CreateCount class property for an instance of the TMyClass class:

procedure ShowCreateCount;
var
   TempInstance: TMyClass;
begin
   TempInstance:=TMyClass.Create;
   try
      ShowMessage(IntToStr(TempInstance.CreateCount));
   finally
      TempInstance.Free;
   end;
end;

The second way is by using a direct class reference:

procedure ShowCreateCount;
begin
   ShowMessage(IntToStr(TMyClass.CreateCount));
end;

The second way is easiest when you don't have an instance of the class available.
Image