Что делает флаг beforefieldinit?

82

Что делает флаг beforefieldinit? Когда я смотрю в IL своего класса, я вижу этот флаг, но я не знаю, что он на самом деле делает?

Embedd_Khurja
источник

Ответы:

132

Смотрите мою статью по этому поводу.

По сути, это beforefieldinitозначает, что «тип может быть инициализирован в любой момент до обращения к каким-либо статическим полям». Теоретически это означает, что его можно очень лениво инициализировать - если вы вызываете статический метод, который не касается каких-либо полей, JIT не нужно инициализировать тип.

На практике это означает, что класс инициализируется раньше, чем в противном случае - это нормально, если он инициализируется в начале первого метода, который может его использовать. Сравните это с типами , которые не были beforefieldinitприменены к ним, когда инициализации типа должна происходить непосредственно перед первым фактическим использованием.

Итак, предположим, у нас есть:

public static void DoSomething(bool which)
{
    if (which)
    {
        FirstType.Foo();
    }
    else
    {
        SecondType.Bar();
    }
}

Если beforefieldinitк ним применяются оба типа (что в C # они делают по умолчанию, если у типа нет статического конструктора), то они оба будут инициализированы в начале DoSomethingметода (обычно - это не гарантируется). Если их нет, beforefieldinitто только один из них будет инициализирован на основе флага.

Вот почему обычно используется статический конструктор (даже пустой!) При реализации шаблона singleton .

Джон Скит
источник
«тип может быть инициализирован в любой момент до ссылки на какие-либо поля». верно ли это и для статических методов?
Рой Намир
@RoyiNamir, в спецификации CLI говорится, что если применяется BeforeFieldInit, то «метод инициализатора типа выполняется при первом доступе к любому статическому полю, определенному для этого типа, или когда-то раньше». Если этот атрибут отсутствует (статический .ctor), тогда «первый доступ к любому статическому полю или полю экземпляра этого типа или первый вызов любого статического, экземпляра или виртуального метода этого типа». Таким образом, это неверно для примененного BeforeFieldInit, если статический метод не ссылается на другое статическое поле.
Arman
3
Я обнаружил, что использование статических конструкторов (т.е. классов, не имеющих флага beforefieldinit) снижает производительность. Если вы часто вызываете статические члены определенного класса, кажется, что среда выполнения должна выполнять дополнительную проверку перед каждым вызовом, чтобы проверить, инициализирован ли тип; beforefieldinit избегает этих проверок. Несколько тестов были примерно на 50% быстрее с beforefieldinit: codeproject.com/Articles/87991/…
Qwertie
6

Похоже, что в 4.6 изменится

https://github.com/dotnet/coreclr/issues/1193

ОмариО
источник
Замечательно, значит ли это, что он будет ждать до самого последнего момента, чтобы инициализировать поле (независимо от того, есть оно beforefieldinitили нет)?
Джеймс Ко
1
Перед первым использованием все обращения будут иметь префикс инициализации. После этого, когда другие методы изменяются, сгенерированный код будет напрямую обращаться к полю. Если это примитивный тип, то его можно даже использовать как постоянную времени JIT.
OmariO
2
Подробный анализ Джона изменений в инициализации типов начиная с .Net 4.0 здесь .
RBT