Если статический член 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
. У Джона Скита есть хорошая статья.