Каков порядок инициализации статической переменной в C #?

Страница DependencyProperty.AddOwner MSDN предлагает пример с двумя classами со статическими членами, а член одного classа зависит от члена другого classа для инициализации. Я думаю, что MSDN ошибочна – порядок инициализации статических переменных ненадежен в C #, как и на C ++ или где-либо еще. Я, вероятно, ошибаюсь, потому что сама библиотека WPF написана так, и все работает отлично. Что мне не хватает? Как компилятор C # может знать безопасный порядок инициализации?

Хорошо, что один тип зависит от инициализации другого типа, если вы не закончите цикл.

В принципе это нормально:

public class Child { static Child() {} // Added static constructor for extra predictability public static readonly int X = 10; } public class Parent { static Parent() {} // Added static constructor for extra predictability public static readonly int Y = Child.X; } 

Результат хорошо определен. Инициализаторы статических переменных Child выполняются до первого доступа к любому статическому полю в classе, согласно разделу 10.5.5.1 спецификации.

Это не так:

 public class Child { public static readonly int Nasty = Parent.Y; public static readonly int X = 10; } public class Parent { public static readonly int Y = Child.X; } 

В этом последнем случае вы либо закончите с Child.Nasty=0 , Parent.Y=10 , Child.X=10 или Child.Nasty=0 , Parent.Y=0 , Child.X=10 зависимости от того, какой class первый доступ.

Parent.Y first к Parent.Y first начнется инициализация Parent . Инициализация Child поймет, что Parent нужно инициализировать, но CLR знает, что он уже инициализирован, поэтому он выполняется независимо, что приводит к первому набору чисел, потому что Child.X заканчивается инициализацией до того, как его значение будет использоваться для Parent.Y .

Доступ к Child.Nasty начнет инициализацию Child сначала, после чего начнется инициализация Parent . Инициализация Parent будет означать, что Child должен быть инициализирован, но CLR знает, что он уже инициализирован, поэтому выполняется независимо, что приводит к второму набору чисел.

Не делай этого.


EDIT: Хорошо, более подробное объяснение, как и было обещано.

Когда тип инициализирован?

Если тип имеет статический конструктор , он будет инициализироваться только после его первого использования (либо при упоминании статического члена, либо при создании экземпляра). Если у него нет статического конструктора, он может быть инициализирован. Теоретически он также может быть инициализирован позже; теоретически вы можете называть конструктор или статический метод без инициализации статических переменных, но он должен быть инициализирован до ссылки на статические переменные.

Что происходит во время инициализации?

Сначала все статические переменные получают свои значения по умолчанию (0, нуль и т. Д.).

Затем статические переменные типа инициализируются в текстовом порядке. Если для выражения инициализатора для статической переменной требуется инициализировать другой тип, то этот тип будет полностью инициализирован до того, как будет присвоено значение переменной – если этот второй тип уже не инициализирован (из-за циклической зависимости). По существу, тип:

  • Уже инициализировано
  • На момент инициализации
  • Не инициализировано

Инициализация запускается только в том случае, если тип не инициализирован. Это означает, что при наличии циклических зависимостей можно наблюдать значение статической переменной до того, как ее начальное значение было назначено . Вот что показывает мой пример Child / Parent .

После выполнения инициализаторов статической переменной выполняется статический конструктор.

См. Раздел 10.12 спецификации C # для получения более подробной информации обо всем этом.


По многочисленным просьбам, вот мой оригинальный ответ, когда я думал, что речь идет о порядке инициализации статических переменных внутри classа :

Статические переменные инициализируются в текстовом порядке в соответствии с разделом 10.5.5.1 спецификации C #:

Инициализаторы статической переменной поля classа соответствуют последовательности назначений, которые выполняются в текстовом порядке, в котором они появляются в объявлении classа.

Обратите внимание, что частичные типы делают это более сложным, так как нет никакого канонического «текстового порядка» classа.

Если вас беспокоит порядок, вы всегда можете разместить свой код в статическом конструкторе. Здесь я регистрирую свои свойства зависимостей.

Нет, я думаю, что ненадежное здесь не правильное слово.

В истинном сценарии одиночного streamа статические члены classа инициализируются, когда в ваш код сначала обращаются к любому из статических членов типа.

Я не знаю c ++, но да только в некоторых случаях, например, в многопоточной среде, если два типа пытаются получить доступ к общему ресурсу, и если это статично, то невозможно сказать, кто победит и какой из них будет работать правильно.

Пример MSDN правильный, и это будет работать правильно.

  • Как сделать управление наложением выше всех других элементов управления?
  • Поиск контроля в элементе управления WPF
  • Добавить параметр в событие Click Button
  • MVVM Передача EventArgs в качестве параметра команды
  • Как настроить таргетинг на все элементы управления (стили WPF)
  • BitmapImage в WPF блокирует файл
  • WindowsFormsHost всегда самый верхний элемент WPF
  • Используйте StyleSelector для кнопки
  • WPF Combobox: различные шаблоны в текстовом поле и выпадающем списке
  • Где я могу получить streamобезопасный CollectionView?
  • Как работает свойство WPF Button.IsCancel?
  • Давайте будем гением компьютера.