Неоднозначность порядка инициализации статических переменных
Во время моего исследования наилучшего способа построения синглтона в C# я наткнулся на следующую статью, где есть краткое упоминание о том, что в C++
"спецификация C++ оставила некоторую неопределенность вокруг инициализации порядок статических переменных."
В конце концов я изучил вопрос и нашел это и это. Где в основном суть (насколько я понимаю) заключается в том, что порядок инициализации статических переменных в C++ не определено. Хорошо, я думаю, что до сих пор так хорошо, но затем я хотел понять следующее утверждение, которое статья позже делает
" К счастью, платформа .NET Framework разрешает эту двусмысленность с помощью своей обработка инициализации переменной."
Итак, я нашел ЭТУ страницу, где говорится
Инициализаторы переменных статического поля класса соответствуют a последовательность заданий, выполняемых в текстовом порядке в которые они появляются в объявление класса.
И приведем пример
using System;
class Test
{
static void Main() {
Console.WriteLine("{0} {1}", B.Y, A.X);
}
public static int F(string s) {
Console.WriteLine(s);
return 1;
}
}
class A
{
static A() {}
public static int X = Test.F("Init A");
}
class B
{
static B() {}
public static int Y = Test.F("Init B");
}
the output must be:
Init B
Init A
1 1
" потому что правила для выполнения статических конструкторов (как определено в Раздел 10.11) обеспечивают, что статический конструктор B (и, следовательно, B статические инициализаторы полей) должны выполняться перед статический конструктор и инициализаторы полей."
Но где я запутался, так это то, что мое понимание состояло в том, что порядок инициализации статических переменных в этих примерах будет основан на том, когда метод или сначала вызывалось поле внутри класса, которое, в свою очередь, основано на порядке выполнения блока кода (в данном случае слева направо). Т. е.: полностью независимо от того, где - или порядок-объявления класса. Тем не менее, моя интерпретация этой статьи говорит, что она является результатом порядка объявления тех классов, которые мое тестирование не поддерживает?
Не мог бы кто-нибудь разъяснить это (и то, что пытается сделать статья) для меня и, возможно, привести лучший пример что неграмотно описанное поведение?
2 ответа:
Это означает, что внутри одного и того же класса статические поля инициализируются в порядке появления в исходном коде. Например:Инициализаторы переменных статического поля класса соответствуют a последовательность заданий, выполняемых в текстовом порядке в которые они появляются в объявлении класса.
class A { public static int X = Test.F("Init A.X"); public static int Y = Test.F("Init A.Y"); }Когда наступает время инициализации статических полей,
Xгарантированно инициализируется доY." потому что правила для выполнения статических конструкторов (как определено в Раздел 10.11) обеспечивают, что статический конструктор B (и, следовательно, B статические инициализаторы полей) должны выполняться перед статический конструктор и инициализаторы полей."
Это означает, что статический конструктор и инициализация членов для каждого класса будут выполняться в порядке вычисления, когда появятся выражения, обращающиеся к этим классам. Относительный порядок появления определений классов в исходном коде не играет никакой роли. роли, даже если они появляются в одном и том же исходном файле (что они, безусловно, не обязаны делать). Например:
static void Main() { Console.WriteLine("{0} {1}", B.Y, A.X); }Предполагая, что ни
A, ниBне были уже статически инициализированы, порядок вычисления гарантирует, что все поляBбудут инициализированы перед любым полемA. Поля каждого класса будут инициализированы в порядке, указанном первым правилом.
1 для целей настоящего обсуждения я игнорирую существование
beforefieldinit.
В языке C++ порядок инициализации переменных со статической длительностью хранения в одной единице трансляции - это порядок, в котором происходят определения таких переменных. Не определено, каков порядок инициализации переменных со статической длительностью хранения в различных единицах трансляции.
То есть стандарт C++ действительно предлагает аналогичную гарантию тому, что вы процитировали, подставляя порядок объявления в классе вместо порядка определения в единственном единица перевода, определяющая такие переменные. Но это не самое главное различие.
В то время как в C++ это единственная гарантия, в C# есть дополнительная гарантия того, что все статические члены будут инициализированы перед первым использованием класса. Это означает, что, если ваша программа зависит от
A(рассмотрим каждый тип в другой сборке, которая является наихудшим случаем), она начнет инициализацию всех статических полей вA, ЕслиAв свою очередь зависит отBдля любого из этих статических полей. инициализации, то инициализацияBстатических членов будет инициализирована там.Сравните это с C++, где во время статической инициализации[*], все остальные переменные со статической длительностью считаются инициализированными. Это главное отличие: C++ предполагает, что они инициализированы, C# гарантирует, что они находятся перед этим использованием.
[*] технически случаем, когда это проблематично, может быть динамическая инициализация в стандарт. Инициализация переменных со статической длительностью хранения внутри каждой единицы трансляции представляет собой двухэтапный процесс, в котором на первом проходе статическая инициализация задает переменным фиксированное постоянное выражение, а затем на втором проходе, называемом динамической инициализацией, инициализируются все переменные со статической памятью, инициализатор которых не является постоянным выражением.