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

Как определяются "примитивные" типы нерекурсивно?

Так как a struct в С# состоит из битов его членов, вы не можете иметь тип значения T, который включает в себя любые поля T:

// Struct member 'T.m_field' of type 'T' causes a cycle in the struct layout
struct T { T m_field; }

Мое понимание заключается в том, что экземпляр вышеуказанного типа никогда не может быть создан * Любая попытка сделать это приведет к бесконечному циклу создания экземпляра/выделения (что, я думаю, приведет к переполнению стека? **) - или, альтернативно, другой способ взглянуть на него может заключаться в том, что само определение не имеет смысла; возможно, это самопровозглашающая сущность, вроде "Это утверждение ложно".

Любопытно, однако, если вы запустите этот код:

BindingFlags privateInstance = BindingFlags.NonPublic | BindingFlags.Instance;

// Give me all the private instance fields of the int type.
FieldInfo[] int32Fields = typeof(int).GetFields(privateInstance);

foreach (FieldInfo field in int32Fields)
{
    Console.WriteLine("{0} ({1})", field.Name, field.FieldType);
}

... вы получите следующий результат:

m_value (System.Int32)

Кажется, мы "лгали" здесь ***. Очевидно, я понимаю, что примитивные типы, такие как int, double и т.д., Должны быть определены каким-то особым образом глубоко в недрах С# (вы не можете определить все возможные единицы внутри системы с точки зрения этой системы... можете ли вы? -другая тема, независимо!); Мне просто интересно узнать , что здесь происходит.

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

Черная магия?


* В отдельном примечании: это правильное слово для типа значения ( "экземпляр" )? Я чувствую, что он носит "ссылочный" коннотации; но, возможно, это только я. Кроме того, мне кажется, что я, возможно, задал этот вопрос раньше - если это так, я забываю, что ответили люди.

** Оба Мартин против Лёвиса и Эрик Липперт указали, что это ни полностью точной, ни соответствующей точки зрения по этому вопросу. См. Их ответы для получения дополнительной информации.

*** Хорошо, я понимаю, что никто на самом деле не лжет. Я не хотел подразумевать, что я думал, что это ложь; мое подозрение было в том, что это было как-то упрощение. После того, как я понял (я думаю) ответ thecoop, это имеет для меня гораздо больше смысла.

4b9b3361

Ответ 1

Насколько я знаю, в пределах сигнатуры поля, которая хранится в сборке, существуют определенные жестко закодированные шаблоны байтов, представляющие примитивные типы "ядро" - целые числа с подписью/без знака и плавающие (а также строки, которые являются ссылочные типы и специальный случай). CLR знает, как справиться с ними. Ознакомьтесь с разделом 23.2.12 раздела II спецификации CLR для битовых шаблонов подписей.

Внутри каждой примитивной структуры ([mscorlib]System.Int32, [mscorlib]System.Single и т.д.) в BCL есть одно поле этого родного типа, и поскольку структура имеет точно такой же размер, как и его составные поля, каждая примитивная структура является одним и тем же битом как его собственный тип в памяти, и поэтому может быть интерпретирован как CLR, С# компилятором, так и библиотеками, использующими эти типы.

Из С#, int, double и т.д. являются синонимами структур mscorlib, каждая из которых имеет свое примитивное поле типа, которое изначально распознается CLR.

(Здесь есть дополнительное усложнение, поскольку спецификация CLR указывает, что любые типы, которые имеют "короткую форму" (родные типы CLR), всегда должны быть закодированы как эта короткая форма (int32), а не valuetype [mscorlib]System.Int32 Таким образом, компилятор С# также знает о примитивных типах, но я не уверен в точной семантике и специальной оболочке, которая продолжается в компиляторе С# и CLR, например, при вызове метода на примитивных структурах)

Таким образом, из-за теоремы Гёдель-неполноты, должно быть что-то "вне" системы, с помощью которой она может быть определена. Это Магия, которая позволяет CLR интерпретировать 4 байта как родной int32 или экземпляр [mscorlib]System.Int32, который псевдоним из С#.

Ответ 2

Мое понимание заключается в том, что экземпляр вышеуказанного типа никогда не может быть создан, любая попытка сделать это приведет к бесконечному циклу создания экземпляра/выделения (который, как я полагаю, вызовет переполнение стека?) - или, альтернативно, другим способом смотреть на это может быть, что само определение просто не имеет смысла;

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

Кажется, нас лгут здесь

Там нет лжи. Int - это структура, содержащая поле типа int. Int имеет известный размер; он по определению четыре байта. Поэтому это юридическая структура, потому что известен размер всех ее полей.

Как тип System.Int32 (например) фактически сохраняет 32-битное целочисленное значение

Тип ничего не делает. Тип - это абстрактное понятие. Вещью, которая делает хранилище, является CLR, и она делает это, выделяя четыре байта пространства в куче, в стеке или в регистрах. Как еще вы предполагаете, что четырехбайтовое целое будет храниться, если не в четырех байтах памяти?

как объект System.Type, на который ссылается typeof (int), представляет собой, как если бы это значение было само обычным полем экземпляра, введенным как System.Int32?

Это просто объект, написанный в коде как любой другой объект. В этом нет ничего особенного. Вы вызываете методы на нем, он возвращает больше объектов, как и любой другой объект в мире. Почему вы думаете, что там что-то особенное?

Ответ 3

Три замечания, в дополнение к ответному ответу:

  • Ваше утверждение о том, что рекурсивные структуры по сути не может работать, не совсем корректно. Это больше похоже на утверждение "это утверждение верно": это правда, если оно есть. Допустимо иметь тип T, единственным членом которого является тип T: например, такой экземпляр может потреблять 0 байтов (поскольку его единственный член потребляет 0 байтов). Рекурсивные типы значений перестают работать, если у вас есть второй член (именно поэтому они запрещены).

  • Посмотрите Моно определение Int32. Как вы можете видеть: на самом деле это тип, содержащий себя (поскольку int является просто псевдонимом для Int32 в С#). Конечно, есть "черная магия" (то есть специальная обложка), как поясняют комментарии: время выполнения будет искать поле по имени и просто ожидать, что он там - я также предполагаю, что компилятор С# будет в особом случае присутствовать int здесь.

  • В сборках PE информация о типе представлена ​​через "типовые сигнальные капли". Это последовательности объявлений типа, например. для сигнатур методов, но также и для полей. Список доступных примитивных типов в такой сигнатуре определен в разделе 22.1.15 спецификации CLR; копия разрешенных значений находится в перечислении CorElementType. По-видимому, API отражения сопоставляет эти примитивные типы с их соответствующими значениями System.XYZ.