|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.|
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 :)
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) DelphiEnough 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"):
However, you CAN specify read and write access specifiers for a property to be class methods.
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:
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 propertiesRecall 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
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:
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
Here's the implementation part of the form's unit:
Let's discuss the code above:
1. The OnClick event handler for Button1 assigns "2005" to the "ClassInt" class property.