Як працює статичний конструктор?


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, я вплутався. Це можливо?


8
Це питання Java або c #? Ви поставили обидва теги, і я не думаю, що специфікація однакова на двох мовах.
ARRG

У моєму відкритті це робота однакова для обох .. Але я хлопець з C # .. Вибачте за це
om471987

4
Java не має статичного конструктора так само, лише статичні блоки для статичної ініціалізації. static {// зробити щось ...}
deraj

2
Особисто мені незручно від будь-якої форми інтерактивності всередині статичного конструктора. Я розумію вашу мету (змусьте кожен метод у цьому статичному класі дочекатися, поки користувач буде авторизований, перш ніж дозволити йому запуститись), але справді не подобається цей спосіб його досягнення.
Брайан

@Brian: - Так ... Ви маєте рацію ... Я просто робив аналіз .. Нарешті я просто вирішив не використовувати конструктор, а метод Initialize
om471987,

Відповіді:


220

Ви задали тут лише одне запитання, але є десяток запитань, які ви мали б задати , тож я відповім на всі.

Ось послідовність, яку я припустив

  1. Початок конструктора класу (також відомого як cctor)
  2. Кінець cctor
  3. початок Main
  4. початок MyMethod

Це правильно?

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

  1. Початок cctor для програми, якщо він є. Немає.
  2. Кінець cctor для програми, якщо він є. Немає.
  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 буде викликаний до завершення MyClass cctor.

Жоден доктор не викликає MyMethod, прямо чи побічно! Тепер чи можливо коли-небудь статичний метод, як MyMethodвиклик, до завершення cctor MyClass?

Ні.

Це все ще правда, навіть якщо задіяно кілька потоків?

Так. Коектор закінчується в одному потоці, перш ніж статичний метод можна буде викликати в будь-якому потоці.

Чи можна cctor викликати більше одного разу? Припустимо, два потоки призводять до запуску cctor.

Гарантовано, що cctor буде викликаний не більше одного разу, незалежно від кількості потоків. Якщо два потоки викликають MyMethod "одночасно", тоді вони перегони. Один з них програє гонку та блокує, поки cctor MyClass не завершить переможну нитку.

Втрачаючий потік блокується, доки cctor не закінчиться? Справді ?

Дійсно.

Так що, якщо cctor у виграшному потоці викликає код, який блокує замок, раніше взятий програним потоком?

Тоді у вас є класичний стан інверсії замовлення. Тупики вашої програми. Назавжди.

Це здається небезпечним. Як я можу уникнути глухого кута?

Якщо вам боляче, коли ви це робите, тоді припиніть це робити . Ніколи не робіть щось, що може заблокувати в cctor.

Чи є гарною ідеєю покладатися на семантику ініціалізації cctor для забезпечення складних вимог безпеки? І чи є гарною ідеєю мати cctor, який здійснює взаємодію користувачів?

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


5
Еріку, мені цікаво, чому ви замінили "static constructor" на "constructor class" або "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 ...
James Michael Hare

11

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

Це можливо, якщо MyMethodвикликається із статичного конструктора або статичного блоку ініціалізації. Якщо ви не викликаєте MyMethodпрямо або побічно зі свого статичного конструктора, з вами все повинно бути.


Як примітка, я розумію, що staticініціалізацію насправді можна викликати перед першим використанням залежно від прийнятності для оптимізації.
James Michael Hare


1
Для статичного конструктора - істина, але для статичної ініціалізації - це було моєю ідеєю. На жаль, можливо, я просто вибирав гніди на фразі "статична ініціалізація не починається ...", що стосується статичної конструкції, але не, якщо клас не має статичного конструктора, тоді статична ініціалізація може відбутися раніше.
James Michael Hare

Вибачте, я, мабуть, просто переаналізую багатослів’я. У контексті запитання це абсолютно правильно, мене просто хвилювало це речення як окремий вираз для статичної ініціалізації в контексті класів без явних статичних конструкторів.
James Michael Hare

@James: Ви не надмірно аналізуєте - тут термінологія є суттєвою відмінністю. Статичні конструктори - це концепція 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

// після натискання клавіші enter

Привіт, вхідні ...

Привіт!

Привіт!


за допомогою системи; простір імен MyNameSpace {клас Програма {static void Main (рядок [] аргументи) {Console.WriteLine ("Введено в main"); 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.