C# два класса со статическими участниками, обращающимися друг к другу

Интересно, почему этот код не заканчивается в бесконечной рекурсии. Я предполагаю, что это подключено к автоматической инициализации статических участников к значениям по умолчанию, но кто-то может сказать мне "шаг за шагом", как делает получение значения 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();
}
18
задан doublep 6 May 2010 в 21:19
поделиться

6 ответов

Я бы предположил:

  • A.a запрашивается, что вызывает срабатывание статического инициализатора A
  • Это вызывает доступ к B.b, что вызывает срабатывание статического инициализатора B
  • A. a запрашивается; инициализатор типа уже активирован (но присвоение еще не произошло), поэтому поле (еще не присвоенное) читается как 0
  • 0 + 1 - 1, которое присваивается B. b <===========================
  • теперь мы выходим из вектора B и возвращаемся в вектор A
  • 1 + 1 - это 2, который присваивается A. a <===========================
  • теперь мы выходим из A cctor
  • 2 возвращается (WriteLine) для A. a
  • мы запрашиваем (на WriteLine) B.b; cctor уже сработал, поэтому мы видим 1
16
ответ дан 30 November 2019 в 07:12
поделиться

Марк прав. Я бы просто добавил к его ответу, что на ваш вопрос отвечает раздел 10.5.5.1 спецификации, в котором говорится:

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

Обратите внимание на последний пункт. Далее в спецификации цитируется ваш точный пример как случай, когда любой порядок разрешен спецификацией; Все гарантии в спецификации заключаются в том, что инициализаторы полей выполняются в текстовом порядке до запуска статических конструкторов. Это не гарантирует, что поля одного типа инициализируются до или после полей другого типа.

Например, jit-компилятору разрешено сказать: «Эй, я вижу, что типы A и B используются впервые в этом методе, который вот-вот будет изменен, позвольте мне воспользоваться моментом, чтобы убедиться, что эти типы загружен.«Джиттеру разрешено выполнять инициализаторы поля в это время, и он может выбрать сначала выполнить A или сначала B по своему усмотрению.

Вкратце: (1) вы не можете полагаться на это поведение ; он определяется реализацией, и (2) спецификация отвечает на ваш точный вопрос; рассмотрите возможность прочтения спецификации, если у вас есть вопрос о семантике языка .

12
ответ дан 30 November 2019 в 07:12
поделиться

Это связано с порядком, в котором вы обращаетесь к статическим свойствам. Первая оценка - A.a. При оценке A.a инициализируется B.b. Поскольку фактическое присвоение a не завершено, значение a остается 0, таким образом, Bb становится 1. После инициализации Bb значение может быть присвоено Aa, то есть 1 + 1, таким образом, 2

6
ответ дан 30 November 2019 в 07:12
поделиться

Интересно, что когда я изменил порядок вывода в вашем примере кода:

    Console.WriteLine("B.b={0} A.a={1}", B.b, A.a);

Я получил противоположные результаты:

B.b=2 A.a=1

Так что похоже, что это делать с порядком доступа к ним

Итак, учитывая, что результат может измениться, добавив раннее использование одной из переменных, похоже, что такие рекурсивно определенные значения - ПЛОХАЯ ИДЕЯ (TM): -)

{ {1}}
2
ответ дан 30 November 2019 в 07:12
поделиться

Поскольку A.a упоминается первым в Console.WriteLine, он загружается первым, что приводит к тому, что B загружается со значением A.a равным 0 => B.b = 1 => A.a становится 2

Переверните печать и посмотрите, как это происходит в другую сторону.

1
ответ дан 30 November 2019 в 07:12
поделиться

Первым загружаемым типом оказывается A. Тип загружается, и его статический член a получает значение по умолчанию, равное нулю. После этого вызывается статический конструктор A. Этот конструктор ссылается на тип B, поэтому B также загружается и вызывается его статический конструктор. Этот конструктор, в свою очередь, ссылается на тип A, но A уже загружен, поэтому здесь ничего не происходит, а b получает значение ноль (текущее значение a) плюс один, что равно единице. После этого статический конструктор B возвращается, а значение a вычисляется.

2
ответ дан 30 November 2019 в 07:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: