0
private class StaticInitTester
        {
            static StaticInitTester() { 

            }

            private StaticInitTester()
            {
                var isNotNull = _mappingFunc != null ? "not" : "";
                Console.WriteLine($"mapping func is {isNotNull} null");
                
            }

            public static StaticInitTester Instance { get; } = new StaticInitTester();
            private static readonly Func<string, string> _mappingFunc = s => s.ToString();

        }


        [TestMethod]
        public void TestStaticInitialization()
        {

            Console.WriteLine(StaticInitTester.Instance);

        }

The output is actually "mapping func is null"

That surprised me since I was under the impression that all static initialization should have already completed once in the non-static constructor of StaticInitTester. When I move the line -

private static readonly Func<string, string> _mappingFunc = s => s.ToString();

To the top of the class then I get

"mapping func is not null"

So , the location of this line seems to be affecting the result.

Is this behavior documented / what's the explanation to this behavior ?

  • 1
    You are causing the static field to be initialized in a non-intuitive order, because your static initialized includes instantiation of the class itself, so introduces a call to the non-static constructor **while static initialization is still in progress**, and so _that_ invocation of the constructor is able to see the class in its partially-initialized state. See duplicate...it's literally the exact same scenario. – Peter Duniho Jun 01 '21 at 20:39

1 Answers1

2

Yes, it is documented. From specification:

The static field variable initializers of a class 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 (Static constructors) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor.

So because Instance is declared before _mappingFunc (in the textual order) it is assigned (and StaticInitTester ctor is called) before the assignment of _mappingFunc which results in observed behaviour.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • 1
    umm.. what? maybe you should reread this. OP's scenario indicates it is **not** executing static field initializers immediately prior to the execution of the static constructor (which isn't indicated by this quote to be textual order sensative - as it is not a variable). Also, this question is observing issue in regards to field initializers as it relates to a `private` constructor. – Brett Caswell Jun 01 '21 at 20:30
  • 1
    @BrettCaswell static field `StaticInitTester Instance` is initialized with call to private ctor `StaticInitTester()` which in it's turn prints the message. If you change order of of the fields the output will be what OP is expecting - "mapping func is not null" (as OP does note in the question), so the reason for this behavior is described in this section of specification - field initializers are executed in textual order. – Guru Stron Jun 01 '21 at 20:39
  • 1
    yep, you're totally right.. I overlooked `public static StaticInitTester Instance { get; } = new StaticInitTester();` static field initialization altogether.. OP wrote "To the top of the class then I get" and I incorrectly assumed he meant he observed it work when moving it above the constructors, but it was really just when above that other field initializer – Brett Caswell Jun 01 '21 at 20:56