20

Dear all, the question like this one has been already asked, but among the answers there was no explanation of the problem which I see.

The problem: the C# Programming Guide says:

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

In particular, static constructor is called before any instance of a class is created. (This doesn't ensure that the static constructor finishes before creation of instance, but this is a different story.)

Let's consider the example code:

using System;

public class Test
{
    static public Test test = new Test();
    static Test()
    {
        Console.WriteLine("static Test()");
    }
    public Test()
    {
        Console.WriteLine("new Test()");
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Main() started");
        Console.WriteLine("Test.test = " + Test.test);
        Console.WriteLine("Main() finished");
    }
}

It outputs:

Main() started
new Test()
static Test()
Test.test = Test
Main() finished

So we can see that the instance constructor finishes (and thus an instance is created) before the static constructor starts. Doesn't this contradict the Guide? Maybe the initialization of static fields is considered to be an implicit part of static constructor?

Community
  • 1
  • 1
Vlad
  • 35,022
  • 6
  • 77
  • 199
  • 1
    Why don't you actually instantiate it outside of the class versus assigning an instance to a static property, then check the order? This looks like a funky way to test it to me. – mway Nov 10 '10 at 23:38
  • 1
    Isn't that because you're instantiating a `static` member...? – rsenna Nov 10 '10 at 23:40
  • @mway: it may work other way, but I would like to understand what happens in exactly this case. – Vlad Nov 10 '10 at 23:42
  • 1
    @SLaks: If I would design the language, I would expect this code to be not compileable, frankly speaking. I know that the dependencies may be indirect and therefore not resolvable at compile time, so I would expect at least a runtime error in this case, in order to guarantee that the static constructor is finished strictly prior to any instance constructors. – Vlad Nov 10 '10 at 23:49
  • My question asked in order to understand what is going on in http://stackoverflow.com/questions/4148169/exception-with-callback-handler-if-a-field-is-an-instance-member – Vlad Nov 10 '10 at 23:52
  • You're right; I take that back. – SLaks Nov 10 '10 at 23:57
  • @rsenna: it definitely is, my thought was however that this behaviour contradicts the specs. – Vlad Nov 11 '10 at 00:22

1 Answers1

29

Inline initializers for static fields run before the explicit static constructor.

The compiler transforms your class into something like this:

public class Test {
    .cctor {    //Class constructor
        Test.test = new Test();                //Inline field initializer
        Console.WriteLine("static Test()");    //Explicit static ctor
    }
    .ctor { ... }    //Instance constructor
}

Note that this is independent of the declaration order.

To quote the spec:

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

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 1
    Yes, this is guaranteed by http://msdn.microsoft.com/en-us/library/79b3xss3.aspx: "Static members are initialized before the static member is accessed for the first time and before the static constructor, if there is one, is called." But doesn't this contradict the first citation from the question? – Vlad Nov 10 '10 at 23:41
  • @Vlad: If the static ctor instantiates the class, it will not finish until it instantiates the class. – SLaks Nov 10 '10 at 23:42
  • @Vlad: no, the first citation just says the static constructor is called *before* the instance constructor, it says nothing about the static constructor being the *first* initialization (which is not). – rsenna Nov 10 '10 at 23:44
  • @SLaks: could you please explain more? Your quote explicitly states that the static field initialization is done before the static constructor, and therefore is not a part of the static constructor. Therefore your quote contradicts to your code example, where the static field initializer is a part of `.cctor`. The Guide says that the static constructor is executed before any instance is created. My test shows that the instance is created before the actual static constructor. – Vlad Nov 10 '10 at 23:47
  • 1
    @Vlad: The field initializers become part of the IL `.cctor` method. They aren't part of the _explicitly declared_ static constructor, but they all get compiled into the same `.cctor`. Your maybe (at the end of the question) is correct. – SLaks Nov 10 '10 at 23:47
  • @SLaks: but doesn't this "maybe" contradict to the bold text in your citation? The citation IMHO implies that the field initializers are distinct from the `.cctor`. – Vlad Nov 10 '10 at 23:51
  • @Vlad: In source, they are. In IL, they're not. – SLaks Nov 10 '10 at 23:57
  • 4
    @Vlad: C# treats field initializers and static constructor as two separate concepts. However, .NET has only one: the *type initializer*, which carries the special name `.cctor`. The C# compiler places both field initializers and static constructor into the .NET *type initializer* for the class. .NET guarantees that the *type initializer* begins running before any instance constructor, and this holds true in your example. When the C# spec tries to separate it into two concepts, it becomes inconsistent. The actual behavior is governed by the CLR spec. – Ben Voigt Nov 10 '10 at 23:59
  • @SLaks: does this mean that the C# prgramming Guide mentions the IL static constructor, not the source one? – Vlad Nov 11 '10 at 00:00
  • @Vlad: Yes. When a program runs, there is only one static ctor. – SLaks Nov 11 '10 at 00:01
  • @SLaks: that's basically mean of the Guide! Thank you for the clarification. – Vlad Nov 11 '10 at 00:03
  • @SLaks: at the same time the same Guide states [here](http://msdn.microsoft.com/en-us/library/79b3xss3.aspx): "Static members are initialized before the static member is accessed for the first time and before the static constructor, if there is one, is called." (I've already cited this one.) So this part of the Guide clearly speaks of source-.cctor, not the IL one. I would say the the Guide is confusingly ambiguous here. – Vlad Nov 11 '10 at 00:09
  • @Vlad: static fields _are_ initialized before both of those conditions. If the static initializer creates an instance of the class, that instance has not been created before the initializer ran. – SLaks Nov 11 '10 at 00:13
  • @SLaks: my last citation was to confirm that the same Guide at different place treats static constructor as a distinct notion, not including the field initializers. As you said already, at the original citation from the same Guide the static constructor _includes_ field initializers. Such an inconsistent usage of the static constructor notion is IMHO ambiguous. – Vlad Nov 11 '10 at 00:17
  • @Ben: Sorry to nitpick your otherwise excellent comment, but the CLI spec *does not* guarantee that the type initializer will run before any instance constructor. This is guaranteed if the type *is not* marked as `BeforeFieldInit` (achieved in C# by having an explicit static constructor), but if the type *is* marked as `BeforeFieldInit` (by having no explicit static constructor in C#) then the type initializer is only guaranteed to run at some point before a static field is first accessed. If a static field is never accessed then the type initializer might never run. – LukeH Nov 11 '10 at 00:23
  • 1
    @Vlad: Yes, the Guide is wrong. It introduces a paradox. Static constructor before instance constructor. Field initializer before static constructor. Instance constructor in the field initializer. @LukeH: That's a very good point, and one I've run into before in C++/CLI. Actually, my comment is perfectly accurate about what the guarantee made by .NET is, but that guarantee only applied to types without `beforefieldinit`, as you correctly point out. A C# class with a C# static constructor is never marked `beforefieldinit`, so the alternate guarantee is not interesting for C#. – Ben Voigt Nov 11 '10 at 00:28
  • @LukeH: And we're talking about classes with static ctors. (and therefore not BeforeFieldInit) – SLaks Nov 11 '10 at 00:30
  • @SLaks, @LukeH: To be perfectly clear, when I said "any instance constructor", I meant "instance constructors for this class regardless of arguments" (all overloaded constructors for this class), not "instance constructors for any type". – Ben Voigt Nov 11 '10 at 00:32
  • @LukeH: I've just seen this article 1 hour ago: http://csharpindepth.com/Articles/General/Beforefieldinit.aspx – Vlad Nov 11 '10 at 00:34
  • @LukeH: thanks for your addition: exact subtle details are sometimes very important! – Vlad Nov 11 '10 at 00:36