Неоднозначность порядка инициализации статических переменных
Во время моего исследования наилучшего способа построения синглтона в 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# гарантирует, что они находятся перед этим использованием.
[*] технически случаем, когда это проблематично, может быть динамическая инициализация в стандарт. Инициализация переменных со статической длительностью хранения внутри каждой единицы трансляции представляет собой двухэтапный процесс, в котором на первом проходе статическая инициализация задает переменным фиксированное постоянное выражение, а затем на втором проходе, называемом динамической инициализацией, инициализируются все переменные со статической памятью, инициализатор которых не является постоянным выражением.