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

Должна ли статическая инициализация поля быть завершена до вызова конструктора?

Следующая программа выдает неправильный вывод.

new A()
_A == null
static A()
new A()
_A == A

Код:

public class A
{
    public static string _A = (new A()).I();

    public A()
    {
        Console.WriteLine("new A()");
        if (_A == null)
            Console.WriteLine("_A == null");
        else
            Console.WriteLine("_A == " + _A);
    }

    static A()
    {
        Console.WriteLine("static A()");
    }

    public string I()
    {
        return "A";
    }
}

class Program
{
    static void Main(string[] args)
    {
       var a = new A();
    }
}

Ответы [ 5 ]

26 голосов
/ 02 апреля 2009

Это правильно.

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

Вот полный путь выполнения:

Когда вы впервые вызываете var a = new A(); в своей программе, это первый раз, когда осуществляется доступ к A.

Это вызовет статическую инициализацию A._A

На этом этапе A._A строит с _A = (new A()).I();

Это хиты


Console.WriteLine("new A()");
if (_A == null)
    Console.WriteLine("_A == null");        

с тех пор _A не было установлено с возвращенным составным типом (пока).

Далее запускается статический конструктор A { static A(); }. Это печатает сообщение «static A ()».

Наконец, ваше исходное утверждение (var a = new A();) выполнено, но на этом этапе статика построена, поэтому вы получите окончательный отпечаток.

3 голосов
/ 14 октября 2010

Еще одна дополнительная заметка - спецификация C # (я смотрю на 4.0, но она есть и в 3.0 тоже) говорит в 10.5.5.1 Инициализация статического поля:

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

У вас есть статический конструктор, поэтому предложение «В противном случае» не применяется. Но я думаю, что для вашего вопроса важна информация о том, что если у вас нет статического конструктора, инициализаторы статического поля могут быть выполнены «в зависимости от реализации». Это может иметь значение, если инициализатор статического поля выполняет какой-либо тип инициализации данных или создания объекта, на который вы полагаетесь, не обращаясь к самому статическому полю.

Я полагаю, что это эзотерика, но я видел, как это произошло сегодня, поскольку «время, зависящее от реализации», похоже, изменилось между C # 3.0 и 4.0 - по крайней мере, для ситуации, на которую я смотрел. Конечно, простое решение простое - просто добавьте статический конструктор ...

1 голос
/ 02 апреля 2009

Я действительно считаю, что он делает то, что вы думаете. Ваш тест затрудняет определение.

Ваша инициализация для _A

public static string _A = (new A()).I();

Сначала создается новый экземпляр A, таким образом, ваши записи новых A () и _A = null. Поскольку это было нулем, когда это началось, поскольку это - инициализация. После инициализации вызывается статический конструктор, который возвращает новый экземпляр.

0 голосов
/ 02 апреля 2009

Кажется, компилятор делает ожидаемое.

1-й - весь статический код выполняется (сначала поля, затем статический конструктор) в классе:

public static string _A = (new A()).I();

// and

static A()
{
    Console.WriteLine("static A()");
}

2nd - конструктор класса называется:

public A()
{
    Console.WriteLine("new A()");
    if (_A == null)
        Console.WriteLine("_A == null");
    else
        Console.WriteLine("_A == " + _A);
}

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

0 голосов
/ 02 апреля 2009

Да, инициализация статических полей должна быть завершена до вызова конструктора. Но вы ставите компилятор в ненормальную ситуацию, и он просто не может подчиняться этому правилу.

Это интересный трюк, но в обычном приложении этого не произойдет.

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