Подтвердить что ты не робот

Как инициализация статического поля работает в С#?

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

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

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();
    }
}
4b9b3361

Ответ 1

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

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

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

При первом вызове var a = new A(); в вашей программе это первый раз, когда к нему обращаются.

Это приведет к отключению статической инициализации A._A

В этот момент A._A строит с _A = (new A()).I();

Это показывает


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

так как в этой точке _A не был задан с возвращенным построенным типом (пока).

Затем запускается статический конструктор A { static A(); }. Это печатает сообщение "статическое A()".

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

Ответ 2

Я действительно считаю, что он делает то, что вы думаете. Тестовый тест трудно сказать.

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

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

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

Ответ 3

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

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

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

// and

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

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

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

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

Ответ 4

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

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

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

Это, по-моему, эзотерическое, но я видел, что это произошло сегодня, так как "время, зависящее от реализации", похоже, изменилось между С# 3.0 и 4.0 - по крайней мере, для ситуации, на которую я смотрел. Легкое решение, конечно, простое - просто добавьте статический конструктор...

Ответ 5

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

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