Static field is initialized later when the class has a static constructor

泪湿孤枕 提交于 2019-12-05 02:18:00

问题


By running this simple code:

class Program
{
    class MyClassWithStatic
    {
        public static int Number = SomeService.GetData();

        static MyClassWithStatic()
        {
            Console.WriteLine("static ctor runs");
        }
    }

    class SomeService
    {
        public static int GetData()
        {
            Console.WriteLine("GetDataRuns");
            return 42;
        }
    }        

    static void Main(string[] args)
    {
        InitService();

        var value = MyClassWithStatic.Number;
        Console.WriteLine(value);
    }

    private static void InitService()
    {
        Console.WriteLine("InitServiceRuns");
    }
}

The output on my machine is this:

InitServiceRuns
GetDataRuns
static ctor runs
42

Meaning first the InitService method is called, then the static field of the MyClassWithStatic is initialized and then the static constructor is called (in fact by looking at this in ILSpy and IlDasm we can see that the initialization of the static fields happens at the beginning of the cctor)

There is nothing interesting at this point, everything makes sense, but when I remove the static constructor of the MyClassWithStatic (so MyClassWithStatic becomes this, and everything else remains as before)

class MyClassWithStatic
{
    public static int Number = SomeService.GetData();
}

The output is this:

GetDataRuns
InitServiceRuns
42

This means by removing the static constructor the static fields are initialized earlier. Since the initialization is a part of the static constructor (I tell this by looking into it with ildasm) the effect is basically that the static constructor is called earlier.

So here is the question:

  1. Can someone explain this behaviour? What can be the reason for this?

  2. Is there any other thing which can change when the static constructor is called? (E.g. attaching a profiler or running it in IIS, etc.) (I compared debug, release mode, x86,x64 and all show the same behavior)

Some general things:

-This was in a .NET 4.6 console application. I also switched to .NET 2 (should run with a different clr, and the behaviour is the same, it doesn't make any difference)

-I also tried this with .NET core: both with and without a cctor the InitService method is called first.

-Now I'm absolutely aware of this page:

The user has no control on when the static constructor is executed in the program.

And I also know that in a static constructor there are many things you shouldn't do. But unfortunately I have to deal with a code where this part is outside of my control and the difference I described makes a huge difference. (And I also went through many C# cctor related SO questions..)

(And Question nr3:) So isn't the whole thing I described a little bit problematic?


回答1:


Can someone explain this behaviour? What can be the reason for this?

@JonSkeet has a paragraph in C# in Depth about static fields and static constructors. Here's a snippet:

The C# specification states:

  • The static constructor for a class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

    • An instance of the class is created.
    • Any of the static members of the class are referenced.

The CLI specification (ECMA 335) states in section 8.9.5:

A type may have a type-initializer method, or not. A type may be specified as having a relaxed semantic for its type-initializer method (for convenience below, we call this relaxed semantic BeforeFieldInit):.

  • If marked BeforeFieldInit then the type's initializer method is executed at, or sometime before, first access to any static field defined for that type.
  • If not marked BeforeFieldInit then that type's initializer method is executed ((at (i.e., is triggered by): first access to any static or instance field of that type, or first invocation of any static, instance or virtual method of that type))

This goes to show you that when a type doesn't have the beforefieldinit flag, the run-time may invoke it in an arbitrary time, given that it is before the first access to any static field defined, which is exactly what you're seeing.

Is there any other thing which can change when the static constructor is called?

The only thing is creating a static type constructor on your type. Otherwises, you have no control over it's invocation.

So isn't the whole thing I described a little bit problematic?

Problematic in what regards? I see no problem as long as you know what you're in for. The CLI specification makes it absolutely clear as to what guarantees you have with a type initializer and without. Thus, if you follow those guidelines there should be no ambiguity.




回答2:


A class with a static constructor will not be marked with the beforefieldinit flag, which allows the runtime to initialize it at a later time (In other words, MyClassWithStatic.Number will be initialized when you first reference/access MyClassWithStatic)

Take a loot at this article for more information.



来源:https://stackoverflow.com/questions/33709210/static-field-is-initialized-later-when-the-class-has-a-static-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!