6

Jon Skeet suggests in his singleton implementation that if you require the maximum laziness for your singleton you should add a static constructor which will make the compiler mark the type as beforefieldinit.

However, I did some testing and it seems that it's more lazy without the beforefieldinit.

Code sample (the private constructor call outputs to console and verifies that the fields were initialized:

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

    public static string Stub()
    {
        return "123";
    }

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    //static Singleton()
    //{
    //}
    private Singleton()
    {
        Console.WriteLine("private ctor");
    }

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

When I call Singleton.Stub() the private constructor is not being hit, and when I uncomment the static constructor, the private constructor is always called.

This is the only difference I could track made by the static constructor.

In my attempts to understand what difference will the beforefieldinit do I've also read Skeet's answer in this post tried it with passing false to DoSomething() - with or without the static constructor the private constructor is not being called.

public static void DoSomething(bool which)
{
    if (which)
    {
        var a = Singleton.Stub();
    }
    else
    {
        Faketon.Stub();
    }
}
Community
  • 1
  • 1
BornToCode
  • 9,495
  • 9
  • 66
  • 83
  • So what's the value of `which` here? Sounds like it's probably `false`. – Jon Skeet May 24 '16 at 14:13
  • The first code sample is not using the DoSomething() method, I simply call `Singleton.Stub()` from Main().. in the second code sample which is always false. – BornToCode May 24 '16 at 14:16
  • That's probably because you pass a constant to the `DoSomething(false)` method and you `if` statement will be optimized, so the `Singleton.Stub()` is optimzed/removed from the code. – Jeroen van Langen May 24 '16 at 14:25
  • @JeroenvanLangen - I tried to uncheck the optimize code from the properties window but I still get the same results. – BornToCode May 24 '16 at 14:36
  • 3
    This question is being discussed on [Meta](http://meta.stackoverflow.com/questions/323706/creating-tags-based-on-people-rather-than-technology-jon-skeet). – Glorfindel May 24 '16 at 14:44

1 Answers1

5

When I call Singleton.Stub() the private constructor is not being hit, when I uncomment the static ctor private constuctor is always called.

It's not clear what the value of which is here, but fundamentally you've got four cases:

  • Static constructor, Singleton.Stub called: type initializer is guaranteed to run
  • Static constructor, Singleton.Stub not called: type initializer is guaranteed not to run
  • No static constructor, Singleton.Stub called: type initializer may run, but isn't guaranteed to
  • No static constructor, Singleton.Stub not called: type initializer may run, but isn't guaranteed to

The last two cases only differ in that if you change Singleton.Stub to use a static field, the third case becomes "type initializer is guaranteed to run".

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1.So why you suggest (in the above quoted article) to add the static ctor for full laziness? When Stub is called without static ctor type initializer may run, with static ctor it will always run - less lazy? 2.`Singleton.Stub not called: type initializer may run, but isn't guaranteed to` - what may cause that the type initializer may run out of the blue? – BornToCode May 24 '16 at 14:29
  • @BornToCode: 1) My fully lazy version uses a static constructor in a nested type which will only be initialized if `Instance` is used. Your code doesn't have a nested type - it's not the same scenario. 2) When a method is first run, it's reasonable (but not required) for the JIT compiler to initialize all types which *might* be needed by the method, and which have lax initialization guarantees. It means it doesn't need to check each time whether the type *has* been initialized. – Jon Skeet May 24 '16 at 14:49