Интересно, почему этот код не заканчивается в бесконечной рекурсии. Я предполагаю, что это подключено к автоматической инициализации статических участников к значениям по умолчанию, но кто-то может сказать мне "шаг за шагом", как делает получение значения 2 и 'b' 1?
public class A
{
public static int a = B.b + 1;
}
public class B
{
public static int b = A.a + 1;
}
static void Main(string[] args)
{
Console.WriteLine("A.a={0}, B.b={1}", A.a, B.b); //A.a=2, B.b=1
Console.Read();
}
Я бы предположил:
A.a
запрашивается, что вызывает срабатывание статического инициализатора A
B.b
, что вызывает срабатывание статического инициализатора B
A. a
запрашивается; инициализатор типа уже активирован (но присвоение еще не произошло), поэтому поле (еще не присвоенное) читается как 0
0
+ 1
- 1
, которое присваивается B. b
<===========================B
и возвращаемся в вектор A
1
+ 1
- это 2
, который присваивается A. a
<===========================A
cctor2
возвращается (WriteLine
) для A. a
WriteLine
) B.b
; cctor уже сработал, поэтому мы видим 1
Марк прав. Я бы просто добавил к его ответу, что на ваш вопрос отвечает раздел 10.5.5.1 спецификации, в котором говорится:
Инициализаторы статических переменных поля класса соответствуют последовательности присваивания, которые выполняются в текстовом порядке , в котором они появляются в объявлении класса. Если в классе существует статический конструктор , выполнение инициализаторов статического поля происходит непосредственно перед выполнением этого статического конструктора. В противном случае , инициализаторы статического поля выполняются в время, зависящее от реализации, до первого использования статического поля этого класса.
Обратите внимание на последний пункт. Далее в спецификации цитируется ваш точный пример как случай, когда любой порядок разрешен спецификацией; Все гарантии в спецификации заключаются в том, что инициализаторы полей выполняются в текстовом порядке до запуска статических конструкторов. Это не гарантирует, что поля одного типа инициализируются до или после полей другого типа.
Например, jit-компилятору разрешено сказать: «Эй, я вижу, что типы A и B используются впервые в этом методе, который вот-вот будет изменен, позвольте мне воспользоваться моментом, чтобы убедиться, что эти типы загружен.«Джиттеру разрешено выполнять инициализаторы поля в это время, и он может выбрать сначала выполнить A или сначала B по своему усмотрению.
Вкратце: (1) вы не можете полагаться на это поведение ; он определяется реализацией, и (2) спецификация отвечает на ваш точный вопрос; рассмотрите возможность прочтения спецификации, если у вас есть вопрос о семантике языка .
Это связано с порядком, в котором вы обращаетесь к статическим свойствам. Первая оценка - A.a. При оценке A.a инициализируется B.b. Поскольку фактическое присвоение a не завершено, значение a остается 0, таким образом, Bb становится 1. После инициализации Bb значение может быть присвоено Aa, то есть 1 + 1, таким образом, 2
Интересно, что когда я изменил порядок вывода в вашем примере кода:
Console.WriteLine("B.b={0} A.a={1}", B.b, A.a);
Я получил противоположные результаты:
B.b=2 A.a=1
Так что похоже, что это делать с порядком доступа к ним
Итак, учитывая, что результат может измениться, добавив раннее использование одной из переменных, похоже, что такие рекурсивно определенные значения - ПЛОХАЯ ИДЕЯ (TM): -)
{ {1}}Поскольку A.a упоминается первым в Console.WriteLine, он загружается первым, что приводит к тому, что B загружается со значением A.a равным 0 => B.b = 1 => A.a становится 2
Переверните печать и посмотрите, как это происходит в другую сторону.
Первым загружаемым типом оказывается A
. Тип загружается, и его статический член a
получает значение по умолчанию, равное нулю. После этого вызывается статический конструктор A
. Этот конструктор ссылается на тип B
, поэтому B
также загружается и вызывается его статический конструктор. Этот конструктор, в свою очередь, ссылается на тип A
, но A
уже загружен, поэтому здесь ничего не происходит, а b
получает значение ноль (текущее значение a
) плюс один, что равно единице. После этого статический конструктор B
возвращается, а значение a
вычисляется.