Это действительно разрешено?
Похоже, что это разрешено спецификацией.
При тестировании в любой нормальной среде выводится 1.
Это верно. Полезно понять причины оптимизации. Цель "расслабленной семантики" состоит в том, чтобы перенести проверку на "запущен ли статический конструктор?" от время выполнения доступа к типу до время выполнения метода, который обращается к типу . То есть, если мы имеем:
void M()
{
blah
if blah blah
... Foo.A ...
if blah blah blah
... Foo.A ...
blah blah blah
}
Предположим, что это время для M
, а cctor Foo
еще не выполнен. Для строгого соответствия джиттер должен генерировать код на при каждом доступе к Foo.A , чтобы проверить, выполнил ли уже Foo
cctor, и выполнить его, если он этого не сделал.
Но если мы сделаем вызов cctor во время jit , jitter знает, что к Foo
обращаются внутри M
, и поэтому может вызвать cctor, когда M
jipped, а затем пропустить генерацию каждой проверки внутри M
Джиттер достаточно умен, чтобы делать правильные вещи при выполнении cctor во время jit; он не выполняет cctor в «неправильном» порядке, как вы описываете, потому что люди, которые написали джиттер, были здравомыслящими людьми, которые просто пытались сделать ваш код быстрее.
Как я должен писать инициализаторы типов, чтобы меня это не укусило?
Вы должны предположить, что авторы соответствующей реализации вменяемы.
Если по какой-либо причине вы не можете предположить, что: вы можете поместить все инициализаторы статических полей, которые вам нужны, в статический конструктор. Компилятор C # не допускает семантику BeforeFieldInit
для типов со статическими конструкторами.