5

I am trying to understand need for static constructors. None of information I found answered following question I have. Why would you do this

class SimpleClass
{
    // Static variable that must be initialized at run time.
    static readonly long baseline;

    // Static constructor is called at most one time, before any
    // instance constructor is invoked or member is accessed.
    static SimpleClass()
    {
        baseline = DateTime.Now.Ticks;
    }
}

as opposed to this

class SimpleClass
{
    // Static variable that must be initialized at run time.
    static readonly long baseline = DateTime.Now.Ticks;

    // Static constructor is called at most one time, before any
    // instance constructor is invoked or member is accessed.
    //static SimpleClass()
    //{

    //}
}

?


This is not dupe of other question, this is about static constructors which don't accept parameters.

  • This is not static specific question, the question is valid for non static initialisation too. – Cyril Gandon Jan 26 '16 at 08:02
  • Possible duplicate of [Initialize class fields in constructor or at declaration?](http://stackoverflow.com/questions/24551/initialize-class-fields-in-constructor-or-at-declaration) – Cyril Gandon Jan 26 '16 at 08:03
  • @CyrilGandon That is not a dupe - this is about static constructors which don't accept parameters. Consider removing the dupe tag. –  Jan 26 '16 at 08:04

2 Answers2

5

The need is somehow obvious: You want to do more than some field initialization for your static members.

Logically if you have this class:

class SimpleClass
{
    // Static variable that must be initialized at run time.
    static readonly long baseline = DateTime.Now.Ticks;


}

You can re-write it to have the same effect:

class SimpleClass
{
    // Static variable that must be initialized at run time.
    static readonly long baseline;

    static SimpleClass () {
        baseline = DateTime.Now.Ticks;
    }   
}

But instead, you could do more in your static constructor such as inspecting (using reflection) some properties and emiting some fast accessors / getters to them, or just simple notify other systems that your type has been created, etc.

From Jeffrey Richter CLR via C# book:

When the C# compiler sees a class with static fields that use inline initialization (the BeforeFieldInit class), the compiler emits the class’s type definition table entry with the BeforeFieldInit metadata flag. When the C# compiler sees a class with an explicit type constructor (the Precise class), the compiler emits the class’s type definition table entry without the BeforeFieldInit metadata flag. The rationale behind this is as follows: initialization of static fields needs to be done before the fields are accessed, whereas an explicit type constructor can contain arbitrary code that can have observable side effects; this code may need to run at a precise time.

Obviouselly there is something more than this happening behind the scenes, I would suggest you to read entire chapter from CLR via C#: "Type Constructors"

George Lica
  • 1,798
  • 1
  • 12
  • 23
  • 5
    Well, they're not quite the same - a class with a static constructor can have different initialization timing compared with a class which only has static field initializers. See http://csharpindepth.com/Articles/General/Beforefieldinit.aspx – Jon Skeet Jan 26 '16 at 08:04
  • can you show example why I need static constructor, what can I do there I can't do in a field initialization? –  Jan 26 '16 at 08:07
  • @JonSkeet Maybe you can answer –  Jan 26 '16 at 08:08
  • 1
    @user200312: Well, the core of the answer here is right: "You want to do more than some field initialization for your static members." For example, maybe you want to log the fact that the type is being initialized. – Jon Skeet Jan 26 '16 at 08:13
  • 1
    "Obviouselly there is something much interesting happening behind the scenes" I don't think that's obvious when you only specified it in terms of a translation. That makes it sound like the two versions are equivalent, when they're not. – Jon Skeet Jan 26 '16 at 08:14
3

This is an example of a possible difference:

class SimpleClass
{
    static readonly long A = DateTime.Now.Ticks;
    static readonly long B = DateTime.Now.Ticks;

    static SimpleClass()
    {
    }
}

A and B are not guaranteed to be the same value, though if you were to write it in the constructor, you could guarantee it:

class SimpleClass
{
    static readonly long A;
    static readonly long B;

    static SimpleClass()
    {
        var ticks = DateTime.Now.Ticks;
        A = ticks;
        B = ticks;
    }
}

In addition, order matters for instantiation of static members.

According to ECMA-334 regarding static field initialization:

The static field variable initializers of a class declaration correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration. If a static constructor (§17.11) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class

So, we can write something like this:

class SimpleClass
{
   public static readonly long A = IdentityHelper.GetNext();
   public static readonly long B = IdentityHelper.GetNext();

   static SimpleClass()
   {
   }
}

public static class IdentityHelper
{
    public static int previousIdentity = 0;
    public static int GetNext()
    {
        return ++previousIdentity;
    }
}

Here, A is guaranteed to be assigned before B. In this example, A will be 1, and B will be 2. We can guarantee that A < B (assuming the identity does not overflow and there's no issues with threading). Now, if we re-order the fields:

public static readonly long B = IdentityHelper.GetNext();
public static readonly long A = IdentityHelper.GetNext();

The functionality changes. Thus, we've created a side-effect which is not immediately clear simply by re-ordering the fields definitions.

A more likely scenario is, we may want to do this:

class SimpleClass
{
   public static readonly long A = IdentityHelper.GetExpensiveResult().A;
   public static readonly long B = IdentityHelper.GetExpensiveResult().B;

   static SimpleClass()
   {
   }
}

Here, we're unable to share GetExpensiveResult() between the fields.

Rob
  • 26,989
  • 16
  • 82
  • 98
  • A tiny spot of the code that not related to the question, `return ++currentIdentity` is better, otherwise you will always have `IdentityHelper.currentIdentity == IdentityHelper.GetNext()` – Cheng Chen Jan 26 '16 at 08:20
  • @Danny Chen, could you elaborate on your last comment ; I'm not quite sure I understand how it worked – Gil Sand Jan 26 '16 at 08:37
  • We can guarantee that A > B- maybe reverse? Not sure I followed that part though –  Jan 26 '16 at 08:40
  • @user200312 Sorry yes, you're right - it was a typo. Should be `A` < `B` – Rob Jan 26 '16 at 09:01
  • @Zil check [this post](http://stackoverflow.com/questions/24853/what-is-the-difference-between-i-and-i), it's about c, but c# is the same. – Cheng Chen Jan 26 '16 at 09:34