In my last post, I hinted at a new native Delphi language feature, class constructors and destructors. For those familiar with, say, C# or VB.NET, they are called a ‘static’ constructor (C#), or a ‘shared’ constructor (VB.NET). Let’s now take a closer look at them in terms of how they’re useful along with a little hint at how they’re implemented. Class constructors had been available in Delphi Prism, but until Delphi 2010, they were notably absent from native Delphi. The main reason for this was that the .NET runtime controlled and defined when a ‘static’ or ‘shared’ constructor was invoked. In native Delphi there was no defined mechanism to do the same.
From the C# documentation:
Called automatically to initialize the class before the first instance is created or any static members are referenced. The constructor cannot be called directly.
From the VB.NET documentation:
Shared constructors initialize a type's shared variables; they are run after the program begins executing, but before any references to a member of the type. A shared constructor specifies the Shared modifier, unless it is in a standard module in which case the Shared modifier is implied.
Since the .NET runtime is a garbage-collected environment, there are no references or mention of ‘class destructors’. Because native Delphi is is not garbage collected and requires explicit destruction of instances and freeing of memory, it stood to reason that for congruency, native Delphi would also have the notion of a class destructor.
First of all let’s look at the syntax:
EDliException = class(Exception)
class constructor Create;
class destructor Destroy;
Technically, it does not matter where in the class declaration they are declared (private, public, protected, published) since, as the documentation above states, “The constructor [destructor (added)] cannot be called directly.” So in this example, I just made them private to merely make it more clear in the source that they cannot be called. Just like the other kinds of ‘class’ methods, the body of theses methods can only access other class methods or variables. They also have no implicit “Self”, so you cannot call virtual class methods in a polymorphic manner. You can have one and only one class constructor/destructor per class type. As we’ll see in a moment, you should treat the class constructor/destructor methods in a manner similar to a unit’s initialization/finalization section. That’s it. It’s pretty simple, really.
Now let’s look at why these are useful. There were several main reasons for adding this to the language. They were:
- Improved encapsulation
- Better control over smart-linking
- Better language compatibility with Delphi-Prism
How many times have you needed to initialize something related to a specific class? What if your class is using the singleton pattern and you need to create that single instance? How about registering the class with a “class-factory?” Prior to Delphi 2010, there were several options available to you, none of which were really in keeping with the OOP notion of encapsulation. You could put all the code into the initialization section of the unit in which the class lived, add a regular class method and call it explicitly, or manually integrate the init code into your application’s startup. By moving all this code into a class constructor, the mere act of “touching” the class will cause the class constructor to run in keeping with the above rules.
Better control over smart-linking
Loosely coupled with improved encapsulation is the notion that by moving any initialization code into the class constructor you have a little more control over what code is linked into your application. You can now take advantage of the fact that as long as the class type isn’t referenced referenced anywhere in your code, none of the code related to the class type is linked in, including the VMT, virtual methods, RTTI, etc.. If you had placed this init code into the initialization section of its containing unit, then at least the VMT, virtual methods and RTTI would have been linked into your application. Even though none of your code references it. The new enhanced RTTI throws an interesting wrinkle to this theory, but that will have to be covered later.
Better language compatibility with Delphi Prism
As Delphi Prism gains popularity, more people will need to maintain some of their code in a manner that they can compile with either compiler. There are still many language constructors that both compilers have that the other doesn’t, but this is a continuing effort to close that gap where it makes sense. Expect to see more work in this area, in both compilers.
In my next post, we’ll “pop the hood” (or bonnet, for our good friends across the pond) and take a peek at how they work and exactly when they are invoked. I will say that many people tend to read way, way too much into this simple statement describing when a class constructor is invoked; “Called automatically to initialize the class before the first instance is created or any static members are referenced.” The only temporal reference in that statement is “…before…”. I will leave you to consider the implications of that word, lest many of you begin to talk about “threading issues” and “code-bloat.” Class constructors and destructors are to classes as initialization and finalization are to units.