Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't the CLR always call value type constructors

I have a question concerning type constructors within a Value type. This question was inspired by something that Jeffrey Richter wrote in CLR via C# 3rd ed, he says (on page 195 - chapter 8) that you should never actually define a type constructor within a value type as there are times when the CLR will not call it.

So, for example (well...Jeffrey Richters example actually), I can't work out, even by looking at the IL, why the type constructor is not being called in the following code:

internal struct SomeValType {     static SomeValType()     {         Console.WriteLine("This never gets displayed");     }     public Int32 _x; } public sealed class Program {     static void Main(string[] args)     {         SomeValType[] a = new SomeValType[10];         a[0]._x = 123;         Console.WriteLine(a[0]._x);     //Displays 123     } } 

So, applying the following rules for type constructors I just can't see why the value type constructor above is not called at all.

  1. I can define a static value type constructor to set the initial state of the type.
  2. A type can have no more than one constructor - there is no default one.
  3. Type constructors are implicitly private
  4. The JIT compiler checks whether the type's type constructor has already been executed in this AppDomain. If not it emits the call into native code, else it doesn't as it knows the type is already 'initialized'.

So...I just can't work out why I can't see this type array being constructed.

My best guess would be that it could be:

  1. The way that the CLR constructs a type array. I would have thought that the static constructor would be called when the first item was created
  2. The code in the constructor is not initializing any static fields so it is ignored. I have experimented with initializing private static fields within the constructor but the field remains the default 0 value - therefore the constructor is not called.
  3. Or...the compiler is somehow optimizing away the constructor call due to the public Int32 being set - but that is a fuzzy guess at best!!

Best practices etc asside, I am just super intrigued by it as I want to be able to see for myself why it doesn't get called.

EDIT: I added an answer to my own question below, just a quote of what Jeffrey Richter says about it.

If anyone has any ideas then that would be brilliant. Many thanks, James

like image 896
jameschinnock Avatar asked Jul 14 '10 12:07

jameschinnock


People also ask

Why static constructor called first in c#?

A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed only once. It is called automatically before the first instance is created or any static members are referenced.

Can static classes have constructors?

They cannot inherit from any class except Object. Static classes cannot contain an instance constructor. However, they can contain a static constructor. Non-static classes should also define a static constructor if the class contains static members that require non-trivial initialization.

Can we initialize static variable in constructor c#?

You can define a static field using the static keyword. If you declare a static variable in a class, if you haven't initialized it, just like with instance variables compiler initializes these with default values in the default constructor. Yes, you can also initialize these values using the constructor.

What is static and private constructor in c#?

Static constructor is called before the first instance of class is created, wheras private constructor is called after the first instance of class is created. 2. Static constructor will be executed only once, whereas private constructor is executed everytime, whenever it is called.


2 Answers

The Microsoft C#4 Spec has changed slightly from previous versions and now more accurately reflects the behaviour that we're seeing here:

11.3.10 Static constructors

Static constructors for structs follow most of the same rules as for classes. The execution of a static constructor for a struct type is triggered by the first of the following events to occur within an application domain:

  • A static member of the struct type is referenced.
  • An explicitly declared constructor of the struct type is called.

The creation of default values (§11.3.4) of struct types does not trigger the static constructor. (An example of this is the initial value of elements in an array.)

The ECMA Spec and the Microsoft C#3 Spec both have an extra event in that list: "An instance member of the struct type is referenced". So it looks as if C#3 was in contravention of its own spec here. The C#4 Spec has been brought into closer alignment with the actual behaviour of C#3 and 4.

EDIT...

After further investigation, it appears that pretty much all instance member access except direct field access will trigger the static constructor (at least in the current Microsoft implementations of C#3 and 4).

So the current implementations are more closely correlated with the rules given in the ECMA and C#3 specs than those in the C#4 spec: the C#3 rules are implemented correctly when accessing all instance members except fields; the C#4 rules are only implemented correctly for field access.

(The different specs are all in agreement -- and apparently correctly implemented -- when it comes to the rules relating to static member access and explicitly declared constructors.)

like image 126
LukeH Avatar answered Sep 28 '22 12:09

LukeH


From §18.3.10 of the standard (see also The C# programming language book):

The execution of a static constructor for a struct is triggered by the first of the following events to occur within an application domain:

  • An instance member of the struct is referenced.
  • A static member of the struct is referenced.
  • An explicitly declared constructor of the struct is called.

[Note: The creation of default values (§18.3.4) of struct types does not trigger the static constructor. (An example of this is the initial value of elements in an array.) end note]

So I would agree with you that the last two lines of your program should each trigger the first rule.

After testing, the consensus seems to be that it consistently triggers for methods, properties, events, and indexers. That means it's correct for all explicit instance members except fields. So if Microsoft's C# 4 rules were chosen for the standard, that would make their implementation go from mostly right to mostly wrong.

like image 38
Matthew Flaschen Avatar answered Sep 28 '22 10:09

Matthew Flaschen