Если статический член readonly вызывает статический метод для получения значения, выполняется ли оно синхронно?

Дано:

public class MyClass
{
    private static readonly Dictionary<string,int> mydict = CreateDictionary();

    private static Dictionary<string,int> CreateDictionary() { ... }
}

Это делается синхронно? (т.е. два быстрых экземпляра MyClass вызывают CreateDictionary() для вызова дважды?

Ответ 1

Да, это потокобезопасно. Безопасен ли поток статического конструктора С#?

Статические конструкторы гарантированно запускаются только один раз для домена приложения до того, как будут созданы какие-либо экземпляры класса или будут доступны какие-либо статические члены. http://msdn.microsoft.com/en-us/library/aa645612.aspx

Инициализация статического поля является частью статического конструктора. Тот факт, что поле readonly ничего не меняет

Некоторое IL-код в соответствии с запросом (взято из Try Roslyn http://goo.gl/ayIMG0)

.method private hidebysig specialname rtspecialname static 
    void .cctor () cil managed 
{
    // Method begins at RVA 0x205f
    // Code size 11 (0xb)
    .maxstack 8

    IL_0000: call class [mscorlib]System.Collections.Generic.Dictionary`2<string, int32> MyClass::CreateDictionary()
    IL_0005: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string, int32> MyClass::mydict
    IL_000a: ret
} // end of method MyClass::.cctor

где .cctor - это специальное имя статических конструкторов. Вызов CreateDictionary и назначение mydict вполне очевидны.

Ответ 2

Принятый ответ правильный; инициализация произойдет либо ноль, либо один раз, но никогда дважды. Но я бы добавил предостережение. В вашем примере CLR и язык С# оставляют за собой право инициализировать поле раньше, чем вы могли ожидать. Если вы пишете:

public class MyClass
{
    private static readonly Dictionary<string,int> mydict = CreateDictionary();
    static MyClass() {}

тогда CLR и С# гарантируют, что поле будет инициализировано, когда первый статический метод вызывается на MyClass или когда создается первый экземпляр MyClass. Если вы опускаете статический конструктор, то CLR и С# разрешены, но не обязательно, для инициализации поля в любое время до этих событий. В частности, предположим, что у вас есть метод M, который вызывает статический метод MyClass. CLR может решить запустить статический инициализатор MyClass.mydict, когда M закодирован, а не когда M фактически вызывает статический метод. Это может в некоторых редких случаях привести к неожиданным результатам.

Сделайте более подробный поиск в поисковой оптимизации beforefieldinit. У Джона Скита есть хорошая статья.