Как работает статический конструктор?

82
namespace MyNameSpace
{
    static class MyClass
    {
        static MyClass()
        {
            //Authentication process.. User needs to enter password
        }

        public static void MyMethod()
        {
            //Depends on successful completion of constructor
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyClass.MyMethod();
        }
    }
}

Вот последовательность, которую я предположил

  1. Запуск статического конструктора
  2. Конец статического конструктора
  3. Начало основного
  4. Запуск MyMethod
  5. Конец основного

Теперь при любом сценарии, если 4 начнутся раньше 2, я облажался. Является ли это возможным?

om471987
источник
8
Это вопрос Java или C #? Вы разместили оба тега, и я не думаю, что спецификации на этих двух языках одинаковы.
ARRG
По-моему, это работает одинаково для обоих .. Но я парень C # .. Простите за это
om471987
4
В Java нет статического конструктора, просто статические блоки для статической инициализации. static {// do something ...}
deraj
2
Лично мне не нравится любая форма интерактивности внутри статического конструктора. Я понимаю вашу цель (заставьте каждый метод в этом статическом классе ждать, пока пользователь не авторизуется, прежде чем разрешить его запуск), но мне действительно не нравится этот метод ее достижения.
Брайан
@Brian: - Да ... Вы правы ... Я просто занимался анализом ... Наконец, я просто решил не использовать конструктор, а метод Initialize
om471987 08

Ответы:

220

Вы задали здесь только один вопрос, но есть около дюжины вопросов, которые вам следовало бы задать, так что я отвечу на них все.

Вот последовательность, которую я предположил

  1. Начало конструктора класса (также известного как cctor)
  2. Конец сектора
  3. начало Main
  4. запуск MyMethod

Это верно?

Нет. Правильная последовательность:

  1. Запуск cctor программы, если он есть. Нет.
  2. Конец программы, если таковая имеется. Нет.
  3. Начало основного
  4. Запуск cctor для MyClass
  5. Конец cctor для MyClass
  6. Запуск MyClass.MyMethod

Что делать, если есть инициализатор статического поля?

В некоторых случаях CLR разрешено изменять порядок, в котором запускаются инициализаторы статических полей. Подробности см. На странице Джона по этой теме:

Различия между статическими конструкторами и инициализаторами типов

Возможно ли, чтобы статический метод MyMethodбыл вызван до завершения работы cctor этого класса?

Да. Если сам cctor вызывает MyMethod, то очевидно, что MyMethod будет вызываться до завершения cctor.

Сектор не вызывает MyMethod. Возможно ли, чтобы статический метод MyMethodбыл вызван до завершения работы cctor MyClass?

Да. Если cctor использует другой тип, cctor которого вызывает MyMethod, то MyMethod будет вызываться до завершения cctor MyClass.

Никакие специалисты не вызывают MyMethod прямо или косвенно! Возможно ли, чтобы статический метод MyMethodбыл вызван до завершения работы cctor MyClass?

Нет.

Это все еще верно, даже если задействовано несколько потоков?

Да. Cctor завершит работу в одном потоке, прежде чем статический метод можно будет вызвать в любом потоке.

Можно ли звонить в cctor более одного раза? Предположим, что оба потока вызывают запуск cctor.

Гарантируется, что cctor будет вызван не более одного раза, независимо от того, сколько потоков задействовано. Если два потока вызывают MyMethod «одновременно», они участвуют в гонке. Один из них проигрывает гонку и блокируется до тех пор, пока MyClass cctor не завершит работу в выигрышном потоке.

Потерявшаяся нить блокируется до тех пор, пока не завершится работа с сектором? Правда ?

В самом деле.

Так что, если cctor в выигрышном потоке вызывает код, который блокирует блокировку, ранее взятую проигравшим потоком?

Тогда у вас есть классическое условие инверсии порядка блокировок. Ваша программа заходит в тупик. Навсегда.

Это кажется опасным. Как избежать тупика?

Если это причиняет боль, прекратите это делать . Никогда не делайте того, что может заблокировать в cctor.

Является ли хорошей идеей полагаться на семантику инициализации cctor для обеспечения выполнения сложных требований безопасности? И хорошая ли идея иметь cctor, который взаимодействует с пользователем?

Тоже не хорошие идеи. Я советую вам найти другой способ гарантировать выполнение предварительных условий ваших методов, влияющих на безопасность.

Эрик Липперт
источник
5
Эрик, мне любопытно, почему в этом ответе вы заменили «статический конструктор» на «конструктор класса» или «cctor». Неправильно ли использовать «статический конструктор» при обращении к cctor?
phoog
6
@phoog: Я хотел быть последовательным в использовании терминологии, поэтому выбрал самый короткий. "Статический конструктор" и "конструктор класса" подходят. В качестве детали реализации статический конструктор типа создается как специальный метод, называемый «.cctor», поэтому обычно такой конструктор называют «cctor». Если бы я писал в более формальном контексте, я бы использовал один из более длинных терминов.
Эрик Липперт,
@EricLippert Верно ли это также для нестатических классов со статическим конструктором?
Legends
2
@Legends: верно ли это и для нестатических классов со статическим конструктором? Да.
Эрик Липперт
24

Согласно MSDN , статический конструктор:

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

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

Теперь, если вы делаете что-то асинхронное в этом static constructor, то ваша задача - синхронизировать это.

Джеймс Майкл Хэйр
источник
7
Если вы делаете что-то асинхронное, что включает в себя второй поток в статическом конструкторе, вас ждет мир боли . Ничто не делает тупик быстрее. См. Пример на stackoverflow.com/a/8883117/88656 .
Эрик Липперт
@Eric: согласен ... Я бы не хотел этого делать, но не был уверен в его примере, что именно он хотел закончить к моменту вызова MyMethod ...
Джеймс Майкл Хэйр,
11

№3 на самом деле №1: статическая инициализация не начинается до первого использования класса, к которому она принадлежит.

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

Сергей Калиниченко
источник
В качестве примечания, насколько я понимаю, staticинициализацию можно вызвать перед первым использованием в зависимости от права на оптимизацию.
Джеймс Майкл Хэйр,
1
Для статического конструктора это правда, но я хотел сказать о статической инициализации. Извините, возможно, я просто выбирал гниды на фразе «статическая инициализация не начинается ...», что верно для статической конструкции, но не, если у класса нет статического конструктора, статическая инициализация может произойти раньше.
Джеймс Майкл Хэйр
Извините, я, наверное, просто переборщил с пустословием. В контексте вопроса это абсолютно правильно, я просто беспокоился об этом предложении как об отдельном заявлении для статической инициализации в контексте классов без явных статических конструкторов.
Джеймс Майкл Хэйр,
@ Джеймс: Ты не переусердствуешь - терминология здесь решающая разница. Статические конструкторы - это концепция C #, а инициализация типов - это вещь .NET. Код внутри статического конструктора (C #) становится частью инициализатора типа (.NET), но когда и как срабатывает триггер инициализатора типа (т. beforefieldinitЕ. Семантика), определяется тем, имеет ли класс C # статический конструктор.
LukeH
9

Из документации (выделено мной):

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

Кен
источник
2

Вы можете гарантировать, что 4 всегда будет после 2 (если вы не создадите экземпляр своего класса из своего статического метода), однако то же самое не верно для 1 и 3.

Скотт Чемберлен
источник
2

Статический конструктор будет вызываться перед выполнением mymethod. Однако, если вы ошиблись, если 4 вызывается перед 2, я предлагаю вам переосмыслить свой дизайн. В любом случае не следует делать сложные вещи в статическом конструкторе.

Умайр
источник
2

CLR гарантирует, что статический конструктор запускается до обращения к статическим членам. Однако ваш дизайн немного пахнет. Проще было бы сделать что-то вроде этого:

static void Main(string[] args) 
{ 
     bool userIsAuthenticated = MyClass.AuthenticateUser();
     if (userIsAuthenticated)
         MyClass.MyMethod(); 
 } 

В вашем дизайне, если аутентификация не удалась, единственный способ предотвратить запуск MyMethod - это выбросить исключение.

фог
источник
2

Гарантируется, что конструктор статического класса был вызван до выполнения любого из его методов. Пример:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Press enter");
        Console.ReadLine();
        Boop.SayHi();
        Boop.SayHi();
        Console.ReadLine();
    }

}

static class Boop
{
    static Boop()
    {
        Console.WriteLine("Hi incoming ...");
    }

    public static void SayHi()
    {
        Console.WriteLine("Hi there!");
    }
}

Вывод:

Нажмите Ввод

// после нажатия Enter

Привет, входящий ...

Всем привет!

Всем привет!

хайю
источник
используя Систему; пространство имен MyNameSpace {class Program {static void Main (string [] args) {Console.WriteLine ("Введено в главную"); Boop.SayHi (); Boop.SayHi (); }} статический класс Boop {static Boop () {Console.Read (); Console.WriteLine («Введен ключ конструктора»); } public static void SayHi () {Console.WriteLine ("Метод вызван"); }}} да, эта программа дает лучшее понимание
om471987
Возможно. Впрочем, в следующий раз опубликуйте это как ответ. Тогда это более полезно и наглядно.
haiyyu
1

Вот фактический порядок, в котором все происходит:

  1. Начало Main
  2. Запуск статического MyClassконструктора
  3. Конец статического MyClassконструктора
  4. Начало MyMethod
  5. Конец Main

источник
0

Или вы можете пройти через отладчик.

парусник
источник