Каков порядок инициализации статической переменной для классов в C #? - PullRequest
10 голосов
/ 10 сентября 2009

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

Ответы [ 3 ]

26 голосов
/ 10 сентября 2009

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

В основном это нормально:

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 выполняются до первого доступа к любому статическому полю в классе, согласно разделу 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 в зависимости от к какому классу обращаются первыми.

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

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

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


РЕДАКТИРОВАТЬ: Хорошо, более подробное объяснение, как и было обещано.

Когда инициализируется тип?

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

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

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

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

  • Уже инициализирован
  • Инициализируется в данный момент
  • Не инициализировано

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

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

Подробнее об этом см. В разделе 10.12 спецификации C #.


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

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

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

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

0 голосов
/ 10 сентября 2009

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

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

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

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

0 голосов
/ 10 сентября 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...