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

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, я облажался. Является ли это возможным?


person om471987    schedule 22.02.2012    source источник
comment
Это вопрос по java или c#? Вы поставили оба тега, и я не думаю, что спецификация одинакова на двух языках.   -  person ARRG    schedule 22.02.2012
comment
На мой взгляд, это работает одинаково для обоих. Но я парень на С#. Извините за это.   -  person om471987    schedule 22.02.2012
comment
Java не имеет статического конструктора таким же образом, только статические блоки для статической инициализации. static { // делаем что-то... }   -  person deraj    schedule 22.02.2012
comment
Лично мне не нравится любая форма интерактивности внутри статического конструктора. Я понимаю вашу цель (заставить каждый метод в этом статическом классе ждать, пока пользователь не авторизуется, прежде чем разрешить его запуск), но мне действительно не нравится этот метод его выполнения.   -  person Brian    schedule 22.02.2012
comment
@Brian:- Да... Ты прав... Я просто проводил анализ... В конце концов я просто решил не использовать конструктор, а использовать метод Initialize.   -  person om471987    schedule 08.03.2012


Ответы (10)


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

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

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

Это правильно?

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

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

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

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

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

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

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

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

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

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

No.

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

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

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

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

Проигравший поток блокируется до тех пор, пока cctor не будет выполнен? Правда?

Действительно.

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

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

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

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

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

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

person Eric Lippert    schedule 22.02.2012
comment
Эрик, мне любопытно, почему в этом ответе вы заменили статический конструктор конструктором класса или cctor. Неправильно ли использовать статический конструктор при обращении к cctor? - person phoog; 23.02.2012
comment
@phoog: я хотел быть последовательным в использовании терминологии, поэтому выбрал самый короткий. Статический конструктор и конструктор класса хороши. В качестве детали реализации статический конструктор типа создается как специальный метод с именем .cctor, поэтому такой конструктор обычно называют cctor. Если бы я писал в более формальном контексте, я бы использовал один из более длинных терминов. - person Eric Lippert; 23.02.2012
comment
@EricLippert Верно ли это и для нестатических классов со статическим конструктором? - person Legends; 15.10.2019
comment
@Legends: Это верно и для нестатических классов со статическим конструктором? Да. - person Eric Lippert; 15.10.2019

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

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

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

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

person James Michael Hare    schedule 22.02.2012
comment
Если вы делаете что-то асинхронное, включающее второй поток в статическом конструкторе, вас ждет мир боли. Ничто не делает взаимоблокировки быстрее. См. пример на stackoverflow.com/a/8883117/88656. - person Eric Lippert; 22.02.2012
comment
@Eric: согласен ... Я бы не хотел этого делать, но не был уверен на его примере, что именно он хотел закончить к тому времени, когда был вызван MyMethod ... - person James Michael Hare; 22.02.2012

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

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

person Sergey Kalinichenko    schedule 22.02.2012
comment
Просто в качестве примечания, насколько я понимаю, инициализация static на самом деле может быть вызвана перед первым использованием в зависимости от права на оптимизацию. - person James Michael Hare; 22.02.2012
comment
Для статического конструктора это правда, но для статической инициализации я имел в виду. Извините, может быть, я просто собирал гниды на фразе «статическая инициализация не начинается...», что верно для статической конструкции, но не в том случае, если класс не имеет статического конструктора, тогда статическая инициализация может произойти раньше. - person James Michael Hare; 22.02.2012
comment
Извините, я, вероятно, просто преувеличиваю количество слов. В контексте вопроса это абсолютно правильно, меня просто беспокоило это предложение как отдельное выражение для статической инициализации в контексте классов без явных статических конструкторов. - person James Michael Hare; 22.02.2012
comment
@James: Вы не слишком анализируете - терминология здесь является решающим отличием. Статические конструкторы — это концепция C#, тогда как инициализация типов — это вещь .NET. Код внутри статического конструктора (C#) становится частью инициализатора типа (.NET), но когда и как срабатывает инициализатор типа (т. е. семантика beforefieldinit) определяется тем, имеет ли класс C# статический конструктор. - person LukeH; 22.02.2012
comment
@JamesMichaelHare: подробное обсуждение того, какие оптимизации разрешены и когда, см. на странице Джона: csharpindepth.com/ Статьи/Общие/Beforefieldinit.aspx - person Eric Lippert; 22.02.2012

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

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

person ken    schedule 22.02.2012

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

person Scott Chamberlain    schedule 22.02.2012

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

person Umair    schedule 22.02.2012

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

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

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

person phoog    schedule 22.02.2012

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

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!");
    }
}

Выход:

Нажмите Ввод

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

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

Всем привет!

Всем привет!

person haiyyu    schedule 22.02.2012
comment
с помощью системы; пространство имен MyNameSpace { class Program { static void Main (string [] args) { Console.WriteLine (введено в main); Буп.Привет(); Буп.Привет(); } } статический класс Boop { static Boop() { Console.Read(); Console.WriteLine(введен ключ конструктора); } public static void SayHi() { Console.WriteLine(метод вызывается); } } } да, эта программа дает лучшее понимание - person om471987; 22.02.2012
comment
Возможно. Однако в следующий раз опубликуйте это как ответ. Тогда это будет более полезным и заметным. - person haiyyu; 22.02.2012

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

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

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

person saille    schedule 29.02.2012