Как работает статический конструктор?
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();
}
}
}
Вот последовательность, которую я предположил
- Начало статического конструктора
- Конец статического конструктора
- Начало основного
- Старт MyMethod
- Конец основного
Теперь в любом случае, если 4 начнется раньше, чем 2, я облажался. Является ли это возможным?
10 ответов
Вы задали здесь только один вопрос, но есть около дюжины вопросов, которые вы должны были задать, поэтому я отвечу на них все.
Вот последовательность, которую я предположил
- Начало конструктора класса (также известный как
cctor
) - Конец cctor
- начало Main
- начало MyMethod
Это правильно?
Правильная последовательность:
- Запуск cctor для Программы, если он есть. Нет.
- Конец cctor для Программы, если он есть. Нет.
- Начало Главного
- Начало cctor для MyClass
- Конец cctor для MyClass
- Начало MyClass.MyMethod
Что делать, если есть инициализатор статического поля?
CLR разрешается изменять порядок, в котором в некоторых случаях запускаются инициализаторы статических полей. См. Страницу Джона на предмете для деталей:
Различия между статическими конструкторами и инициализаторами типов
Это когда-нибудь возможно для статического метода, как
MyMethod
быть вызванным до того, как cctor этого класса завершится?
Да. Если сам cctor вызывает MyMethod, то, очевидно, MyMethod будет вызываться до завершения cctor.
Cctor не вызывает MyMethod. Это когда-нибудь возможно для статического метода, как
MyMethod
быть вызванным до того, как cctor MyClass завершится?
Да. Если cctor использует другой тип, чей cctor вызывает MyMethod, то MyMethod будет вызываться до завершения cctor MyClass.
Никакие актеры не называют MyMethod, прямо или косвенно! Теперь это возможно для статического метода, такого как
MyMethod
быть вызванным до того, как cctor MyClass завершится?
Нет.
Это все еще верно, даже если вовлечено несколько потоков?
Да. Cctor завершится в одном потоке, прежде чем статический метод может быть вызван в любом потоке.
Можно ли вызвать актер более одного раза? Предположим, что два потока вызывают запуск cctor.
Cctor гарантированно будет вызван не более одного раза, независимо от того, сколько потоков задействовано. Если два потока вызывают MyMethod "одновременно", они участвуют в гонке. Один из них проигрывает гонку и блокирует, пока cctor MyClass не завершится в выигрышном потоке.
Потерянная нить блокируется, пока не закончится cctor? Действительно?
В самом деле.
Так что, если cctor в выигравшем потоке вызывает код, который блокирует блокировку, ранее принятую проигравшим потоком?
Тогда у вас есть классическое условие инверсии порядка блокировки. Ваша программа блокируется. Навсегда.
Это кажется опасным. Как я могу избежать тупика?
Если тебе больно, когда ты это делаешь, прекрати это делать. Никогда не делайте то, что может заблокировать в cctor.
Является ли хорошей идеей полагаться на семантику инициализации cctor для обеспечения сложных требований безопасности? И это хорошая идея иметь cctor, который взаимодействует с пользователем?
Ни хорошие идеи. Мой совет заключается в том, что вы должны найти другой способ обеспечить выполнение предварительных условий для ваших методов, влияющих на безопасность.
Согласно MSDN, статический конструктор:
Статический конструктор вызывается автоматически для инициализации класса перед созданием первого экземпляра или ссылками на любые статические члены.
Таким образом, статический конструктор будет вызываться перед статическим методом MyClass.MyMethod()
вызывается (при условии, что он также не вызывается во время статической конструкции или инициализации статического поля).
Теперь, если вы делаете что-то асинхронное в этом static constructor
тогда ваша работа - синхронизировать это.
№ 3 на самом деле #1: статическая инициализация не начинается до первого использования класса, к которому она принадлежит.
Возможно, если MyMethod
вызывается из статического конструктора или статического блока инициализации. Если вы не вызываете MyMethod
прямо или косвенно от вашего статического конструктора, все будет в порядке.
Из документации (выделено мое):
Статический конструктор вызывается автоматически для инициализации класса перед созданием первого экземпляра или ссылками на любые статические члены.
Статический конструктор будет вызываться перед выполнением mymethod. Однако, если вы облажались, если 4 вызывается раньше 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
Привет, входящий...
Всем привет!
Всем привет!
Вы можете гарантировать, что 4 всегда будет идти после 2 (если вы не создаете экземпляр вашего класса из статического метода), однако это не так для 1 и 3.
CLR гарантирует, что статический конструктор будет запущен до того, как будут получены какие-либо статические члены. Тем не менее, ваш дизайн немного вонючий. Было бы проще сделать что-то вроде этого:
static void Main(string[] args)
{
bool userIsAuthenticated = MyClass.AuthenticateUser();
if (userIsAuthenticated)
MyClass.MyMethod();
}
В вашей схеме, если аутентификация не удалась, единственный способ предотвратить запуск MyMethod - вызвать исключение.
Вот фактический порядок, в котором все идет вниз:
- Начало
Main
- Начало статического
MyClass
конструктор - Конец статики
MyClass
конструктор - Начало
MyMethod
- Конец чего-либо
Main