1. Technology
Send to a Friend via Email
Simulating class properties in (Win32) Delphi
While (Win32) Delphi enables you to create class (static) methods (function or procedure), you cannot mark a property of a class to be a class (static) property. False. You can! Let's see how to simulate class properties using typed constants.
Join the Discussion
"Post your views, comments, questions and doubts to this article."
Discuss!
Related Resources
Simulating multicast events in Win32 Delphi
Class Methods
Typed constants
OOP in Delphi
Language features in Delphi for .NET

When you drop a button on a form, you create an instance of a TButton class. When you drop another button on a form, you create a second (separate) instance of a TButton class. Setting the Caption property of the first button will not change the Caption property of the second button (as expected). Each instance of the TButton class stores its own properties (Caption, Enabled, Font, etc.)

What if you wanted, for a certain property, to be shared across any given instance? This property is not associated with a particular instance, it is shared among instances. What's more, why not access this property without even creating an instance of a class!

Warning: Object-Oriented-Programming talk ahead :)
In Delphi OOP jargon, a method of a class is either an instance method or a class method. To access an instance method, you must first create an instance (object) of a class. To access a class method you do not need an instance.
While Delphi enables you to create class methods, declaring class properties is not a language feature.

A class (static or shared) property is a property you can access without actually creating an instance of a class. A class property is not associated with a specific instance. Since each property should come with one private field, no matter how many instances of a class are created, there is only one copy (and therefore, value) of a class field (and therefore, property value).

While class fields, properties and methods exist in Delphi for .Net, class properties are not supported in Win32 Delphi (up to version 7 and Delphi 2005 for Win32).

Why there are NO class properties in Delphi? What, there are? How?

Class Properties in (Win32) Delphi
Enough OOP discussions in the article, let's see some code...
In order to simulate a class property, we first need to create a typed constant in the implementation section of the class declaration. This typed constant mimics the private field that a normal property uses to read or write the data to (to hide the implementation logic).

Recall that creating a class method involves adding a language keyword "class" in front of the method name. This is NOT possible with properties. In other words, the code below will fail to compile (with "[Error] myObjectUnit.pas: PROCEDURE or FUNCTION expected"):

type
  TMyObject = class
   class property PropName : integer ....
  end;

However, you CAN specify read and write access specifiers for a property to be class methods.

type
  TMyObject = Class
  private
    class function GetClassInt: integer;
    class procedure SetClassInt(const Value: integer);
  public
    property ClassInt : integer read GetClassInt 
                                write SetClassInt;
  end;

In the above code, the (integer) property is called "ClassInt" and getter and setter are called "GetClassInt" and "SetClassInt". Those two class methods are used to access the typed constant that mimics the private field.

Here's the full source to a sample class holding one class property:

unit myObjectUnit;

interface

type
  TMyObject = Class
  private
    class function GetClassInt: integer;
    class procedure SetClassInt(const Value: integer);
  public
    property ClassInt : integer read GetClassInt 
                                write SetClassInt;
  end;

implementation

{$WRITEABLECONST ON}
const TMyObject_ClassInt : integer = 0;
{$WRITEABLECONST OFF}

class function TMyObject.GetClassInt: integer;
begin
  result := TMyObject_ClassInt;
end;

class procedure TMyObject.SetClassInt(
  const Value: integer);
begin
  TMyObject_ClassInt := value;
end;

end. //unit myObjectUnit

Warning: using typed constants could lead into name / value clashes. Due to unit scoping, when you define more that one class per unit, be sure NOT to accidentally change the value of the typed constant from another class. This is why we prefixed the "ClassInt" with "TMyObject" when naming our typed constant ("TMyObject_ClassInt").

Using class properties
Recall that, in order to call a class method you also need to specify the class name. If a class "TMyClass" would have a class method named "MyClassMethod(...)" then you would call this method like "TMyClass.MyClassMethod(...)". Unfortunately, the same approach CANNOT be used on our class property.

Accessing class properties
The first idea would be to try accessing the "ClassInt" property as in:

TmyObject.ClassInt := 2005;

This will not compile, with "[Error] Unit1.pas(): Method identifier expected".

Obviously we need a variable of type TMyObject, so the next code will compile at design-time:

procedure TForm1.Button1Click(Sender: TObject);
var 
  myObject : TMyObject;
begin
  myObject.ClassInt := 2005;
end;

What will happen at run time? An Access Violation? Interestingly NO!

Thank you Delphi compiler! When you try to access the "ClassInt" property even from an un-initialized variable, Delphi will simply pass a call to property getter and setter methods, since those are class methods a valid instance is NOT needed. Both the GetClassInt and SetClassInt are operating on the type constant (unit global variable) - and no exception is raised.

Testing class properties
To test our class property, let's drop three buttons (TButton) on a form: Button1, Button2 and Button3.

Here's the implementation part of the form's unit:

....
implementation

uses myObjectUnit;

procedure TForm1.Button1Click(Sender: TObject);
var
  myObject : TMyObject;
begin
  myObject.ClassInt := 2005;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  myObject : TMyObject;
begin
  ShowMessage(IntToStr(myObject.ClassInt));
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  myObjectInstanced : TMyObject;
begin
  myObjectInstanced := TMyObject.Create;
  try
    myObjectInstanced.ClassInt := 7;
    ShowMessage(IntToStr(myObjectInstanced.ClassInt));
  finally
    myObjectInstanced.Free;
  end;
end;

Let's discuss the code above:

1. The OnClick event handler for Button1 assigns "2005" to the "ClassInt" class property.
2. Clicking on Button2 will show the value of the ClassInt property. It will display "2005" - even though the two "myObject" variables are defined in separate events.
3. Finally, the code in the OnClick event handler for Button3 actually creates one instance of the TMyObject class. Reassigns a value to the ClassInt property and displays it.

p.s.
Interested in Simulating multicast events in Win32 Delphi?

©2014 About.com. All rights reserved.