Статический конструктор вызывается до ссылки на какие-либо статические члены

Согласно документам:

Статический конструктор используется для инициализации любых статических данных или для выполнения определенного действия, которое необходимо выполнить только один раз. Он вызывается автоматически перед созданием первого экземпляра или ссылкой на любые статические элементы.

Но я увидел в сообщении stackoverflow следующую цитату из спецификации С#:

Если в классе существует статический конструктор (§10.12), выполнение инициализаторов статического поля происходит непосредственно до выполнения этого статического конструктора.

Это противоречит, я не понимаю, что на первом месте, статический конструктор или инициализация статического члена.


person T.S    schedule 12.03.2017    source источник
comment
После компиляции инициализаторы статических членов стали частью статического конструктора и размещались прямо перед явным телом статического конструктора.   -  person user4003407    schedule 13.03.2017


Ответы (3)


Рассмотрим этот класс:

public static class TestStatic
{
    public static int SomeValue = GetValue();

    static TestStatic()
    {
        Console.WriteLine("Constructor");
    }

}

И этот вспомогательный метод:

public static int GetValue()
{
    Console.WriteLine("GetValue");
    return 5;
}

Если вы запустите этот код:

Console.WriteLine(TestStatic.SomeValue);

Результат, который вы получите:

GetValue
Constructor
5

Таким образом, вы можете видеть, что оба утверждения, которые вы опубликовали, верны. Конструктор вызывается перед обращением к статическому члену (SomeValue), а инициализатор статического поля вызывается перед конструктором.

person DavidG    schedule 12.03.2017
comment
спасибо, я запутался с упоминаемым термином и инициализацией статического члена. - person T.S; 13.03.2017

Оба утверждения, которые вы упомянули, верны и совпадают друг с другом. Я не уверен, почему вы думаете, что они противоречат друг другу.

Порядок выполнения следующий:

  1. Статические поля с инициализаторами.
  2. Статический конструктор.

Вышесказанное соответствует вашему второму утверждению. В первом утверждении просто упоминается, когда эти действия выполняются, т.е. до:

  1. Создается первый экземпляр класса.
  2. Ссылаются на любые статические члены.

На самом деле, приведенные выше условия можно считать гарантией для статических конструкторов, что инициализаторы статических полей будут выполнены, а статический конструктор будет вызван до того, как произойдет любое из двух событий (оператор 1).

В спецификации C# четко упоминается:

Статический конструктор класса выполняется не более одного раза в данном домене приложения. Выполнение статического конструктора инициируется первым из следующих событий, происходящих в домене приложения: Создается экземпляр класса. Ссылаются на любой из статических членов класса. Если класс содержит метод Main (раздел 3.1), с которого начинается выполнение, статический конструктор для этого класса выполняется до вызова метода Main. Если класс содержит какие-либо статические поля с инициализаторами, эти инициализаторы выполняются в текстовом порядке непосредственно перед выполнением статического конструктора.

Просто добавить, поскольку вы подчеркнули -

до создания первого экземпляра или ссылки на любые статические элементы.

Это просто означает, что можно ссылаться на статический член класса даже до создания экземпляра этого класса. Таким образом, даже в этом случае статический конструктор будет вызываться перед доступом к статическому члену.

Если вы видите код DavidG в его ответе, даже если класс не создается, но есть ссылка на один из статических членов, все равно инициализация статического поля происходит до того, за чем следует выполнение статического конструктора.

Пример:

public static class Test
{
    public static int i = 10;
    public static int j = new Func<int>(() => { 
            Console.WriteLine("Static field initializer called."); return 20; 
        })();
    static Test()
    {
        Console.WriteLine("Static Constructor called.");
    }
}

Теперь, если вы выполните:

Console.WriteLine(Test.i);

Вы получите следующий вывод:

Вызывается инициализатор статического поля.

Вызывается статический конструктор.

10

person Shakti Prakash Singh    schedule 13.03.2017
comment
спасибо за исчерпывающее объяснение. я проголосовал за ваш ответ. - person T.S; 13.03.2017

Причина такого поведения (и это задумано) заключается в том, что инициализация статических полей на самом деле происходит как часть статического конструктора. Компилятор добавляет к логике статического конструктора все выражения, которые у вас есть для инициализаторов статических полей.

person Artak    schedule 12.03.2017