Почему CLR не всегда вызывает конструкторы типа стоимости

StackOverflow https://stackoverflow.com/questions/3246078

Вопрос

У меня есть вопрос относительно конструкторов типа в типе значения . Этот вопрос был вдохновлен чем-то, что Джеффри Рихтер писал в CLR Via C # 3RD Ed, он говорит (на странице 195 - Глава 8), что вы никогда не должны определить конструктор типа в пределах типов значения, так как есть время, когда CLR не позвонит Это.

Так, например, (ну ... Джеффри Рихтерс Пример на самом деле), я не могу работать, даже, глядя на IL, почему конструктор типа не вызывается в следующем коде:

internal struct SomeValType
{
    static SomeValType()
    {
        Console.WriteLine("This never gets displayed");
    }
    public Int32 _x;
}
public sealed class Program
{
    static void Main(string[] args)
    {
        SomeValType[] a = new SomeValType[10];
        a[0]._x = 123;
        Console.WriteLine(a[0]._x);     //Displays 123
    }
}
.

Так, применяя следующие правила для конструкторов типа, я просто не вижу, почему конструктор типа Value выше не вызывается вообще.

  1. Я могу определить конструктор типа статического значения, чтобы установить начальное состояние типа.
  2. Тип может быть не более одного конструктора - нет по умолчанию.
  3. тип конструкторов неявно частный
  4. Compiler JIT проверяет, был ли конструктор типа типа уже выполнен в этом Appdomain. Если не это излучает вызов на нативный код, иначе он не знает, что тип уже «инициализирован».
  5. Так ... я просто не могу решить, почему я не вижу, что этот тип массива построен.

    Мое лучшее предположение было бы, что это может быть:

    1. То, как CLR создает массив типа. Я бы подумал, что статический конструктор будет вызван, когда был создан первый элемент
    2. Код в конструкторе не инициализирует какие-либо статические поля, поэтому он игнорируется. Я экспериментировал с инициализацией частных статических полей внутри конструктора, но поле остается значением 0 по умолчанию - поэтому конструктор не вызывается.
    3. или ... компилятор каким-то образом оптимизирует вызов конструктора из-за общего набора Int32 - но это нечеткое предположение в лучшем случае !!
    4. Наилучшие практики и т. Д. Всреднико, я просто очень заинтригован, так как я хочу видеть для себя, почему он не звонит.

      Редактировать: я добавил ответ на свой вопрос ниже, просто цитата того, что Джеффри Рихтер говорит об этом.

      Если у кого-то есть идеи, то это было бы блестяще. Большое спасибо, Джеймс

Это было полезно?

Решение

Microsoft C # 4 немного изменился из предыдущих версий, и теперь более точно отражает поведение, которое мы видим здесь:

11.3.10 Статические конструкторы

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

    .
  • статический элемент типа структуры ссылается.
  • вызывается явный заявленный конструктор типа структуры.

Создание значений по умолчанию (& # 167; 11.3.4) типов структуры не вызвать статический конструктор. (Ан Пример этого является начальное значение элементов в массиве.)

the the Спецификация ECMA и Microsoft C # 3 SPEC У обоих есть дополнительное событие в этом списке: «Участник экземпляра ссылается элемент структуры типа« Тип структуры ». Так выглядит так, как будто C # 3 был в нарушении собственного спецификации здесь. Спец C # 4 был приведен в более тесное выравнивание с фактическим поведением C # 3 и 4.

Редактировать ...

После дальнейшего расследования, кажется, что в значительной степени все экземпляры доступа к элементу , кроме доступа к прямому полему, за исключением статического конструктора (по меньшей мере, в текущих реализациях Microsoft C # 3 и 4). .

Таким образом, текущие реализации более тесно связаны с правилами, приведенными в спецификациях ECMA и C # 3, чем в спецификации C # 4: правила C # 3 реализуются правильно при доступе к членам всех экземпляров / EM> поля; Правила C # 4 предназначены только правильно реализованы для доступа поля.

(разные спецификации все в согласии - и, по-видимому, правильно реализовано - когда речь идет о правилах, касающихся статического доступа к участнику и явно объявленные конструкторы.)

Другие советы

из §18.3.10 стандарта (см. также язык программирования C # Книга):

Исполнение статического конструктора для структуры запускается первым из следующих событий, возникающих в пределах домена приложения:

    .
  • член экземпляра структуры ссылается.
  • статический член структура ссылается.
  • явный объявленный конструктор структура называется.

[ Примечание : Создание значений по умолчанию (& # 167; 18.3.4) структуры Типы не вызывают статическую конструктор. (Пример этого начальное значение элементов в массив.) END ПРИМЕЧАНИЕ ]

Так что я согласен с вами, что последние две строки вашей программы должны каждый запускать первое правило.

После тестирования консенсус, похоже, он последовательно запускает методы, свойства, события и индексаторы. Это означает, что правильно для всех явных элементов экземпляра , кроме полей . Поэтому, если правила C # 4 Microsoft были выбраны для стандарта, это сделало бы, чтобы их реализация была бы в основном вправо в основном неправильно.

Просто вкладывая это как «ответ», чтобы я мог поделиться тем, что сам мистер Рихтер писал об этом (у кого-нибудь есть ссылка на последнюю разработку CLR, кстати, легко получить издание 2006 года, но найти его немного сложнее получить новейшую):

Для такого рода вещей, как правило, лучше смотреть на спецификацию CLR, чем спецификация C #. CLR SPEC говорит:

4. Если не помечено ранее, то метод инициализатора этого типа выполнен в (I.E., срабатывает):

• Первый доступ к любому статическому полю этого типа или

• Первый вызов любого статического метода этого типа или

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

• Первый вызов любого конструктора для этого типа.

Поскольку ни один из этих условий не удовлетворен, статический конструктор составляет не , вызывает. Единственные хитрые части к примечанию состоят в том, что «_x» - это поле экземпляра, а не статическое поле, а построение массива структуров делает не , вызывает любые конструкторы экземпляра на элементах массива.

Еще один интересный образец:

   struct S
    {
        public int x;
        static S()
        {
            Console.WriteLine("static S()");
        }
        public void f() { }
    }

    static void Main() { new S().f(); }
.

Обновление: Мое наблюдение - это то, что если статическое состояние не будет использоваться, статический конструктор никогда не будет затронут - что-то время выполнения, кажется, решается и не относится к ссылочным типам. Это просит вопрос, если он оставлен ошибка, потому что оно мало влияет, это по дизайну, или это в ожидании ошибки.

UPDATE 2: Лично, если вы не делаете что-то фанк в конструкторе, это поведение от времени выполнения никогда не должно вызывать проблему. Как только вы получите доступ к статическому состоянию, он ведет себя правильно.

UPDATE3: дальше к комментарию Lukeh и ссылается на ответ Мэтью Флэстгена, реализацию и вызов собственного конструктора в структуре, также вызывает вызов статическим конструктором. Это означает, что в одном из трех сценариев поведение не то, что он говорит о жере.

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

internal struct SomeValType
    {
        public static int foo = 0;
        public int bar;

        static SomeValType()
        {
            Console.WriteLine("This never gets displayed");
        }
    }

    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            // Doesn't hit static constructor
            SomeValType v = new SomeValType();
            v.bar = 1;

            // Hits static constructor
            SomeValType.foo = 3;
        }
    }
.

Примечание в этой ссылке Указывает, что статические конструкторы не вызывают, когда просто доступ к экземплярам:

http://www.jaggersoft.com/pubs/structsvsclasses.htm#defsclasscles.htm#default

Я думаю, что вы создаете массив вашего типа стоимости.Таким образом, новое ключевое слово будет использоваться для инициализации памяти для массива.

Это действительно, чтобы сказать

SomeValType i;
i._x = 5;
.

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

array[i] = new SomeRefType();
.

Это безумный поведение атрибута «Forefieldinit» в Msil. Это влияет на C ++ / CLI, я подал отчет об ошибках, в котором Microsoft очень красиво объяснил, почему поведение - это то, как оно указано, и я указал несколько разделов в стандарте языка, который не согласился / необходимо обновить, чтобы описать фактическое поведение Отказ Но это не публично видно. Во всяком случае, вот окончательное слово на нем от Microsoft (обсуждение аналогичной ситуации в C ++ / CLI):

С тех пор, как мы вызываем стандарт Здесь линия от раздела I, 8.9.5 говорит это:

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

Этот раздел на самом деле входит в подробности о том, как языковая реализация можно выбрать, чтобы предотвратить поведение Вы описываете. C ++ / CLI выбирает не чтобы скорее они позволяют программисту сделать это, если они пожелают.

в основном, поскольку код ниже имеет Абсолютно нет статических полей, джит полностью правильно просто не Призывающие статические конструкторы класса.

То же поведение - это то, что вы видите, хотя на другом языке.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top