Closed ohads-MSFT closed 1 year ago
The latter implies that the user does have some control over when the static constructor is executed, in that it would be executed immediately before accessing any property of the type, or invoking any of its static methods, but not before. Would that be a correct statement? If so, please clarify this in the docs.
Hi @ohads-MSFT
No, the user doesn't have any control over when the static constructor is executed.
§14.12 Static constructors, has the normative language for when a static constructor is invoked.
The key text is:
The static constructor for a closed class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
- An instance of the class is created.
- Any of the static members of the class are referenced.
The language doesn't restrict the compiler or runtime from invoking a static constructor in the absence of either of those events.
I'll add this to the backlog, and add the help-wanted label. The fix is:
Also, related to #32809
@BillWagner the standard text you quoted actually sounds like it does restrict the compiler/runtime from such an optimization, because it seems to provide a closed list.
In other words, The execution of a static constructor is triggered by the first of the following events
for me sounds equivalent to The execution of a static constructor is triggered ONLY by the first of the following events
.
The examples that follow also support this understanding in my view:
because the execution of A’s static constructor is triggered by the call to A.F, and the execution of B’s static constructor is triggered by the call to B.F.
Which implies these (and only these) are the triggers for static ctor execution.
@ohads-MSFT
You can't assume the word ONLY is in the standard. It's not there, and no such restriction is required. The standard says:
The static constructor for a closed class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
There is no rule that says a static constructor won't be called in the absence of those triggering events.
It may help resolve this to learn why that information is important to you.
Adding @jaredpar to this thread.
@BillWagner When I see "triggered by" I read that as "only triggered by", otherwise it's not a trigger IMHO - but sure I could be wrong, let's wait for Jared's response... Note that the community seems to think as I do: https://stackoverflow.com/questions/1437352/when-is-a-static-constructor-called-in-c
As for my use case, we're using a library that reads some app settings from a static constructor. We want to inject some values to that configuration at runtime from a place we know for sure happens before the static ctor has been executed.
@BillWagner interesting, wasn't aware of those - are they guaranteed to happen before static constructors? Specifically, if my assembly references assembly X will my assembly's module initializer be executed before any static ctor of the referenced X assembly (assuming X's types aren't accessed / invoked)?
The docs you linked to don't seem to explicit guarantee that...
are they (module initialzers) guaranteed to happen before static constructors?
Yes, other than in truly pathological scenarios (like another module initializer creating an instance of the type with the static constructor).
Adding @RikkiGibson to be sure my answer is correct.
In case we get validation, it would just translates into yet another docs clarification request (this time for module initializers) 😎
I would refer to https://github.com/dotnet/runtime/blob/main/docs/design/specs/Ecma-335-Augments.md#module-initializer.
A module initializer is executed at, or sometime before, first access to any static field or first invocation of any method defined in the module.
If you control the startup of the app, you can probably ensure your module initializer runs before the static constructor in the library in question.
Do take care that the above document refers to the platform concept of the module initializer. A module initializer defined in C# is a static method on a type. If that particular type has a static constructor, it will run before the module initializer. For example, in a scenario like this.
Also, when a compilation has multiple module initializers in C#, the order in which they run is implementation-defined. (We really do this by emitting the platform-level module initializer during compilation, and calling each of the user's module initializers.)
reopening this based on https://github.com/dotnet/docs/issues/33704#issuecomment-1409226234
@RikkiGibson thank you for clarifying! Any thoughts about the original question regarding static constructor execution guarantees ?
@BillWagner apologies but I don't follow, the associated fix (https://github.com/dotnet/docs/pull/37691) only seems to relate to module initializers. As far as I can tell, my original question about static constructors hasn't been officially answered...
Recall that AFAIK most of the community agrees with my understanding (including Jon Skeet / @jskeet for example), who quoted the exact same line in the spec about static ctor triggering, interpreting it as I did: https://stackoverflow.com/questions/32525628/why-static-constructor-not-called-before-first-call-to-class-method/32525769#comment52909611_32525769
Hi @ohads-MSFT
You can rely on the standard language. I don't want to add a stronger guarantee in the less formal language reference. That just adds two sources of truth that might drift over time.
Note that there is a little wiggle room in the standard language, but I really doubt the runtime would make any changes here.
Hi @ohads-MSFT
You can rely on the standard language. I don't want to add a stronger guarantee in the less formal language reference. That just adds two sources of truth that might drift over time.
My claim is that adding "only triggered by" would actually not be a stronger guarantee than the standard provides... surely, we could involve some C# standard authority who could settle this?
My claim is that adding "only triggered by" would actually not be a stronger guarantee than the standard provides... surely, we could involve some C# standard authority who could settle this?
Sure. You can write an issue in the dotnet/csharpstandard repo and the committee as a whole will take it up.
@ohads-MSFT
I suspect we all agree that docs should not codify current behavior as standard.
The ECMA standard is the standard. Sometimes it is written in spec language that doesn't follow all the implications we may expect. In this case, adding an implication to the docs, when the spec would have said "only" had that been intended. If the spec needs to change that is a separate and careful process for good reason.
If I understand, you want to set some settings before the static initializer runs. Running things first is the purpose of module initializers. Is that working out for you?
If I understand, you want to set some settings before the static initializer runs. Running things first is the purpose of module initializers. Is that working out for you?
To be honest I don't even remember why I started this whole discussion :) But it definitely sounds like module initializers would do the trick.
The ECMA standard is the standard. Sometimes it is written in spec language that doesn't follow all the implications we may expect. In this case, adding an implication to the docs, when the spec would have said "only" had that been intended. If the spec needs to change that is a separate and careful process for good reason.
Fair enough, I'll comment in those StackOverflow threads to try and let them know they are all (even @jskeet who modified said standard page multiple times) apparently mistaken...
The docs state:
However one bullet below the above states:
The latter implies that the user does have some control over when the static constructor is executed, in that it would be executed immediately before accessing any property of the type, or invoking any of its static methods, but not before. Would that be a correct statement? If so, please clarify this in the docs.
In other words, the docs currently provide guarantees on the latest time a static ctor would be executed, but it's not clear whether there are any guarantees about the earliest time it would be executed (e.g. it will NOT be executed before any property/method is accessed/invoked).
Document Details
⚠ Do not edit this section. It is required for learn.microsoft.com ➟ GitHub issue linking.