"There are times when the CLR will not call a value type's static type constructor"

May 15, 2008

Teaching Managed Runtime Environment’s course, I find one more feature of .Net framework, that I didn’t realised. This time I just stand with something that Jeffrey Richter says in one of the “Important Boxes” of its book “Clr via C#”. He writes “ipsis verbis”:  “…. there are times when the CLR will not call a value type's static type constructor”. Will not???? Surpreendido There are times???? This means sometimes will, sometimes will not? Lets check better.

 

Then he shows an example using a SomeValType array, which exemplifies this behaviour:

 

internal struct SomeValType {

  static SomeValType() {

    Console.WriteLine("This never gets displayed");

  }

  public Int32 m_x;

}

public sealed class Program {

  public static void Main() {

    SomeValType[] a = new SomeValType[10];

    a[0].m_x = 123;

    Console.WriteLine(a[0].m_x); // Displays 123

  }

}

 

If we run this example we will not see the message: "This never gets displayed”. Ok. If we change SomeValType from struct (which defines a value type) to class (which defines a reference type), we will see the message: "This never gets displayed". Of course, we also have to instantiate each array’s element before manipulating it, or it will throw a NullPointerException (we must do: a[0] = new SomeValType(); before: a[0].m_x = 123;).

 

This makes me launch a lot of questions:

- So what’s the difference between value and reference types, which makes CLR doesn’t run static type constructor for value types?

- Why an array for this explanation?

- Is there any relation between this behaviour and the usage of an array?

 

So I make other experiences and we can reproduce the same behaviour without that array. For the same SomeValType we can do:

 

public sealed class Program {

  public static void Main() {

    SomeValType v = new SomeValType(); // = new SomeValType() is optional for value types.

    v.m_x = 123;

    Console.WriteLine(v.m_x); // Displays 123

  }

}

 

For this test we will have the same results. The message "This never gets displayed" is just displayed when SomeValType is a reference type.

 

So, according to Ecma-335 specification when it was supposed the Jit Compiler emits a call to execute a static type constructor?

 

For this SomeValType type, which defines explicitly a static type constructor (meaning that SomeValType is not marked BeforeFieldInit), Jeffrey Richter writes:

« The JIT compiler can emit the call immediately before code that would create the first instance of the type or immediately before code that accesses a noninherited field or member of the class»

 

The second proposition is true, but the first one is not. Independently if SomeValType is a value or a reference type, in both cases ‘vholds an instance of that type. This contradicts the preposition: “…emit the call immediately before code that would create the first instance of the type.”

 

The really difference is that:

- In first case (being SomeValType a value type) the statement new SomeValType() emits (by C# compiler) an initobj SomeVT and this statement doesn’t call any instance constructor.

- But when SomeValType is a reference type, then the same statement new SomeValType(), will emit a newobj SomeVT::.ctor() and this one will call an instance constructor.

 

This difference between calling, or not, an instance constructor makes Jit Compiler emitting, or not, a call to execute a static type constructor. And this is different from creating “the first instance of the type.

 

This behaviour follows the Ecma-335 specification which says:

«If not marked BeforeFieldInit then that type’s initializer method is executed at (i.e., is triggered by):

  first access to any static field … or first invocation of any static method of that type or

  first invocation of any constructor for that type

 

Finally if I change SomeValType definition inserting an instance constructor:

internal struct SomeValType {

  static SomeValType() {

    Console.WriteLine("This never gets displayed");

  }

  SomeValType(int n){ m_x = n;}

  public Int32 m_x;

}

public sealed class Program {

  public static void Main() {

    SomeValType v = new SomeValType(5);

    v.m_x = 123;

    Console.WriteLine(v.m_x); // Displays 123

  }

}

 

Now independently if SomeValType is a value or a reference type, we will get the same behaviour and the message: "This never gets displayed", will always be displayed. Because in both cases there is allways a call to an instance constructor.