Jump to content
wuwuxin

Question about Delphi class (static) constructor

Recommended Posts

I am somewhat unclear about Delphi class constructor. Say, for the following code:

TMyFunkyObject = class
strict private
	class constructor Create;
public
	class procedure FoBar;
end;

I am calling TMyFunkyObject.FoBar from the initialization section of another unit.

 

 

Is it guaranteed that the class constructor will be (implicitly) called before TMyFunkyObject.FoBar?  Or the sequence is in-deterministic?

Share this post


Link to post

Class constructors are called from the initialization section of the unit where the class is declared.

 

So in your scenario it depends on the initialization order, and therefore it is not guaranteed that the class constructor is called before class methods are called. 

Share this post


Link to post

A generic class constructor can behave even weirder.

https://docwiki.embarcadero.com/RADStudio/Sydney/en/Methods_(Delphi)#Class_Constructors

Quote

Note: Even though the compiler takes care of ordering the initialization of classes, in some complex scenarios, ordering may become random. This happens when the class constructor of a class depends on the state of another class that, in turn, depends on the first class.

Note: The class constructor for a generic class or record may execute multiple times. The exact number of times the class constructor is executed in this case depends on the number of specialized versions of the generic type. For example, the class constructor for a specialized TList<String> class may execute multiple times in the same application.

 

  • Thanks 1

Share this post


Link to post

From an old blog post from Allen Bauer (https://blog.therealoracleatdelphi.com/2009/09/:

"

    • All eligible class constructors and class destructors are invoked in sequence with unit initialization and finalization, respectively.
    • If a given class constructor or destructor is eligible to be invoked (ie. it was linked into your application), it will run immediately before the initialization section for the unit in which the class is implemented. The class destructors will be invoked immediately after the finalization section for the unit in which the class is implemented.
    • Class constructors in a given unit are generally invoked in the same order of declaration, except in cases described below. Class destructors are invoked in reverse order from the class constructors.
    • For an ancestor class declared in the same unit, its class constructor will be invoked before the descendant class constructor and the class destructor is invoked after the descendant class destructor.
    • If the implementation of given class constructor references another class in the same unit with a class constructor, the referenced class’ class constructor will be invoked before the current class’ class constructor. If the references are cyclic (ie. they reference each other in their class constructors), then they are invoked in reverse order of declaration. This means that there can be cases where a class constructor can reference an “unconstructed” class.
    • Ancestor classes from external units used in the interface section are guaranteed to already have their class constructors run prior to any class constructors on descendant classes within the current unit.
    • Unit cycles can break down the deterministic nature of the above rules in the same manner as unit initialization and finalization. However, for a given unit, it is guaranteed that all the class constructors declared within in it will have already run immediately before the initialization section runs. Congruent to this rule is that it is guaranteed that all the class destructors will run immediately after the finalization section.
    • Dynamically loaded packages, using LoadPackage. Because there is no way to know exactly which classes are going to be used, just like there is no way to know which units are going to be used, all class constructors and destructors along with all unit initialization and finalizations are invoked according to the above rules.

Other rules about their use are:

    • You do not have to declare a class destructor if you declare a class constructor, and vice versa.
    • They cannot be virtual, dynamic or message.
    • They cannot be explicitly called.
    • They cannot have any parameters.
    • They do not have to be called Create and Destroy. (ie. Init and Fini are equally valid names).

With this implementation, it was easier to leverage the same table that the compiler creates for unit initialization and finalization. It satisfies this requirement: “Called automatically to initialize the class before the first instance is created or any static members are referenced.” Issues with cycles are also clearly warned against in VB.NET and C#: “Avoid circular references in Shared … since it is generally impossible to determine the order in which classes containing such references are loaded.”

Another benefit is that since it is running during the initialization/finalization phases, any threading implications are no different than the existing rules regarding unit initialization and finalization."

 

What we don't know is, if the compiler behavoiur has changed since then.

  • Like 2
  • Thanks 1

Share this post


Link to post
16 hours ago, David Heffernan said:

So in your scenario it depends on the initialization order, and therefore it is not guaranteed that the class constructor is called before class methods are called. 

@David Heffernan

 

Thanks you.   If Unit A is where the class (that has a class constructror) is defined, and Unit B is where the static class method is called (in Unit B's initialization section) - that is,  Unit B references Unit A. Does that mean Unit A  initialization come BEFORE unit B?

Share this post


Link to post
1 hour ago, wuwuxin said:

@David Heffernan

 

Thanks you.   If Unit A is where the class (that has a class constructror) is defined, and Unit B is where the static class method is called (in Unit B's initialization section) - that is,  Unit B references Unit A. Does that mean Unit A  initialization come BEFORE unit B?

Not necessarily. For instance if your dpr file references them in the other order then it won't be that way. And other unit use clauses can confound you. 

 

Enforcing dependencies in initialisation order is not robust. I prefer to find a different way to solve such problems. 

  • Like 1

Share this post


Link to post
Guest
On 8/31/2021 at 9:41 AM, Fr0sT.Brutal said:

*facepalm* is it really so hard to run init sections in the order of unit appearance?

Sometimes when juggling multiple mt-enabled 3rd party libs it is an absolute necessity.

Just include units (from different libs, own code et.al) in the dfm, in the needed order and put a comment in the dfm to explain why this is key.

Also - (slightl unrelated) - always minimize the IDE "auto dfm stuff" like managing main form(s).

IMHO any IDE "help" is just for introduction. Serious projects should not allow the IDE to edit code at all if not specifically requested by the dev.

Share this post


Link to post
15 hours ago, Dany Marmur said:

Sometimes when juggling multiple mt-enabled 3rd party libs it is an absolute necessity.

Just include units (from different libs, own code et.al) in the dfm, in the needed order and put a comment in the dfm to explain why this is key.

Also - (slightl unrelated) - always minimize the IDE "auto dfm stuff" like managing main form(s).

IMHO any IDE "help" is just for introduction. Serious projects should not allow the IDE to edit code at all if not specifically requested by the dev.

I guess you mean dpr not dfm. But even that sometimes is not enough. Because the units won't be initialised in the order the appear in the dpr. The first unit in the dpr will use other units and that influences initialisation order too. 

Share this post


Link to post
On 8/31/2021 at 2:43 AM, Lars Fosdal said:

Circular references create a lot of fun.

You have a twisted sense of humor, sir.  😉

Share this post


Link to post
17 hours ago, Bill Meyer said:

You have a twisted sense of humor, sir.  😉

It exists to balance out my inherent sense of imminent doom.

  • Haha 1

Share this post


Link to post
Guest
On 9/2/2021 at 8:54 AM, David Heffernan said:

I guess you mean dpr not dfm. But even that sometimes is not enough. Because the units won't be initialised in the order the appear in the dpr. The first unit in the dpr will use other units and that influences initialisation order too. 

Yes, i did dfm, and very true. It's not a perfect strategy. Thanks for the clarification.

Share this post


Link to post
6 hours ago, Lars Fosdal said:

It exists to balance out my inherent sense of imminent doom.

I see. But that sense of doom is just a constant.

Share this post


Link to post
On 8/31/2021 at 6:39 PM, David Heffernan said:

Enforcing dependencies in initialisation order is not robust. I prefer to find a different way to solve such problems. 

 

I was thinking about this thread today and was curious - what is your different way to solve the initialization order problem?

 

Share this post


Link to post
On 9/24/2021 at 10:50 AM, Darian Miller said:

 

I was thinking about this thread today and was curious - what is your different way to solve the initialization order problem?

 

You're trying to use a default mechanism that was defined for the convenience of the IDE and basic run-time system.

 

If it doesn't work for some of your units or objects, you'd probably want to somehow "register" your objects that have an ordering dependency with something of your own creation (a dictionary or directory of some kind), then initialize them in whatever order you need. Similar to what you'd get using a DI container.


 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×