2009-05-19 4 views
2

У вас есть какие-либо идеи, почему следующий код:Интересная функция со статическими полями. StackOverflow исключение

public class A 
{ 
    public static int i = B.i + 1; 
} 

public class B 
{ 
    public static int i = A.i + 1; 
} 

Наличие:

 int aa = A.i; 
     int bb = B.i; 

Говорит, что аа = 2 (!!!) и бб = 1.

У меня в голове голова НАЗАД !!! Насколько я понимаю, рекурсия останавливается на статических методах, но почему? Если вы переделаете int i в геттеры (чтобы отлаживать и понимать, почему на земле это работает так), вы получаете исключение переполнения стека.

+0

Полиморфизм особенность? – bzlm

+0

Не знаю, почему это изменилось. –

ответ

6

исполнение Нет сомнений в том, что происходит так:

B.i статический инициализатор работает первый, и устанавливает B.i = A.i + 1. Поскольку A.i еще не был инициализирован, A.i равен default(int), что равно 0. B.i получает значение 1.

Статический инициализатор A.i работает второй и устанавливает A.i = B.i + 1 = 2.

Поскольку статические инициализаторы запускаются в неопределенном порядке, вы можете обнаружить, что любое другое поле равно 2 каждый раз, когда вы запускаете его, или что они переключаются при внесении других структурных изменений в ваш код. Однако всегда нужно начинать 2, и всегда должно быть 1.

P.S. Это не имеет ничего общего с полиморфизмом.

EDIT: Для некоторого дальнейшего понимания сроков статических инициализаторов и конструкторов, вы можете изучить this relevant portion of the C# specification.

+0

ОК нет ничего общего с полиморфизмом. , но почему он не попадает в рекурсивный вызов ??? – 0100110010101

+0

Нет рекурсивного вызова (если вы не измените его как свойство getter.) Инициализаторы полей запускаются один раз и выполняют свою работу; они не являются «вызываемыми». – mquander

+0

Я имею в виду, что когда инициализатор B.i (например) хочет значение A.i, он не вызывает A.i и говорит: «эй, вы вычислите значение, пожалуйста». Он просто смотрит на то, что A.i действительно в памяти. Перед запуском инициализатора A.i это означает, что A.i равно 0. – mquander

4

Поле не похоже на свойство getter. Он просто хранит данные, а не какую-либо операцию. Фактически, инициализация будет перенесена на другой метод (статический конструктор, .cctor), который инициализирует статические переменные в классе.

В начале оба значения равны 0. Прежде чем вы получите доступ к A.i в первый раз, метод .cctor для класса A работает. Он пытается получить доступ к B.i в первый раз, что вызовет выполнение статического конструктора класса B. .cctor из B попытается получить доступ к A.i, но так как это не первый раз, когда обращается к полю, статический конструктор A будет не бежать больше. Он просто извлекает текущее значение A, которое остается 0. Следовательно, B.i будет равен 1, когда B..cctor закончит выполнение, и управление возвращается на A..cctor. Он увидит значение B.i и добавит 1 к нему и сохранит его в A.i. Таким образом, A.i будет равен 2 (1 + 1) и B.i будет равен 1.

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

1

Вот что происходит.

  • A Доступен в первый раз.
  • A.i создан со значением 0.
  • Статический инициализатор для A работает.
  • B доступен в первый раз.
  • Доступ к A.i. В настоящее время она имеет значение 0.
  • Bi устанавливается в 1. Ai устанавливается равным 2.

Действительно важно отметить, что статические свойства инициализации по умолчанию и затем статический запускается инициализатор.

Лично я избегаю статической инициализации, статического состояния и остального как можно больше. Слишком много ошибок.

Смежные вопросы