Как это то, что enum получает от System.enum и является целым числом одновременно?

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

Вопрос

Редактировать: Комментарии внизу. Также, это.


Вот что такое путаница меня. Мое понимание в том, что если у меня есть enum, как это ...

enum Animal
{
    Dog,
    Cat
}

... что я по сути, определяется тип значения называется Animal с двумя определенными значениями, Dog а также Cat. Отказ Этот тип получает от Тип ссылки System.Enum (Что-то, какие типы ценностей не могут нормально делать - по крайней мере, не в C # - но, который разрешен в этом случае), и имеет объект для литья взад и вперед до / из / из int значения.

Если то, как я только что описал, что тип Enum выше, были правдой, то я ожидал, что следующий код бросает InvalidCastException:

public class Program
{
    public static void Main(string[] args)
    {
        // Box it.
        object animal = Animal.Dog;

        // Unbox it. How are these both successful?
        int i = (int)animal;
        Enum e = (Enum)animal;

        // Prints "0".
        Console.WriteLine(i);

        // Prints "Dog".
        Console.WriteLine(e);
    }
}

Как обычно, Вы не можете обновить тип значения из System.Object как все, кроме его точного типа. Так как можно вышеизвестно? Как будто Animal тип является ан int (не просто конвертируемый к int) а также является ан Enum (не просто конвертируемый к Enum) в то же время. Это многократное наследование? Делает System.Enum Как-то наследовать System.Int32 (что-то я бы не ожидал возможным)?

Редактировать: Это не может быть либо из вышеперечисленного. Следующий код демонстрирует это (я думаю) убежительно:

object animal = Animal.Dog;

Console.WriteLine(animal is Enum);
Console.WriteLine(animal is int);

Вышеуказанные выходы:

ИСТИНА ЛОЖЬ

Обе Документация MSDN на перечислениях и спецификация C # использует термин «базовый тип»; Но я не знаю, что это значит, и я когда-либо не слышал, это используется в отношении чего-либо, кроме врата. Что делает «базовый тип» на самом деле иметь в виду?


Итак, это еще один случай, который получает специальное лечение от CLR?

Мои деньги на это дело ... Но ответ / объяснение было бы хорошим.


Обновлять: DAMIEN_THE_UNBELIEVER. предоставил ссылку на действительно ответить на этот вопрос. Объяснение можно найти в разделе II спецификации CLI, в разделе на enums:

Для целей привязки (например, для определения определения метода из ссылки на метод, используемый для него, используемого его), отчетливо отличается от их базового типа. Для всех остальных целей, включая проверку и исполнение кода, Unboxed Enum свободно взаимозависимо от его базового типа. Отказ Enums можно вспомогать к соответствующему типу экземпляра в корпусе, но этот тип нет так же, как в корпусе типа базового типа, поэтому бокс не теряет исходного типа enum.

Редактировать (снова?!): Ждать, на самом деле, я не знаю, что я прочитал это прямо в первый раз. Может быть, это не на 100% объясняет специализированное поведение несполнения (хотя я оставляю ответ Дэмиен, как принято, поскольку он пролил много света по этому вопросу). Я буду продолжать смотреть в это ...


Другое редактирование: Человек, тогда Ответ Yodaj007 бросил меня за другую петлю. Как-то enum не совсем так же, как int; И все же А.Н. int может быть назначен на переменную enum без литья? Бух?

Я думаю, что это все в конечном итоге, освещенным Ответ Hans, Вот почему я принял это. (Извините, Дэмиен!)

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

Решение

Да, специальное лечение. Compiler JIT остро осознает способ работы типов стоимости в корпусе. Что в целом то, что делает типы значений немного шизоидами. Бокс включает в себя создание значения System.Object, которое ведет себя точно так же, как значение ссылочного типа. В этот момент значения типа стоимости больше не ведут себе, как значения делают во время выполнения. Что позволяет, например, иметь виртуальный метод, такой как TOSTRING (). Объект в штучной упаковке есть указатель таблицы метода, так же, как типы ссылок.

Компилятор JIT знает указатели таблиц метода для типов значений, такие как INT и Bool Front. Бокс и распаковки для них очень Эффективно, требуется, но несколько инструкций машинного кода. Это должно было быть эффективным назад в .NET 1.0, чтобы сделать его конкурентоспособным. А. очень Важная часть этого является ограничением, что значение типа значения может быть неbableed только к одному и тому же типу. Это позволяет избежать джиттера, чтобы создать массивное оператор выключателя, которое вызывает правильный код преобразования. Все, что нужно сделать, это проверить указатель таблицы метода в объекте и убедитесь, что это ожидаемый тип. И скопируйте значение прямо из объекта напрямую. Примечательно, возможно, это то, что это ограничение не существует в VB.NET, его CType () Оператор на самом деле делает Code для функции помощника, которая содержит это сообщение большого выключателя.

Проблема с типом Enum заключается в том, что это не может работать. Enums может иметь другой тип getunderlyingtype (). Другими словами, значение Unboxed имеет разные размеры, чтобы просто копировать значение из вспомогательного объекта не может работать. Очень знайте, что джиттер больше не встроен на Ungobing код, он генерирует вызов в функцию помощника в CLR.

Этот помощник назван jit_unbox (), вы можете найти его исходный код в источнике SSCLI20, CLR / SRC / VM / Jithelpers.cpp. Вы увидите это, имеющие дело с типами Enum специально. Это разрешительно, он позволяет распаться с одного типа enum на другой. Но только если основной тип такой же, вы получаете InvalidCastException, если это не так.

Что также является причиной того, что Enum объявляется как класс. Его логично Поведение имеет тип ссылочного типа, полученные типы Enum могут быть отлиты от одного к другому. С вышеупомянутым отмеченным ограничением о совместимости базового типа. Значения типа ENUM, однако, очень сильно поведение значения значения типа. У них есть копировальная семантика и поведение бокса.

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

Enums специально относятся к CLR. Если вы хотите перейти в детали горы, вы можете скачать MS Partition II. спецификация В нем вы обнаружите, что Enums:

Enums подчиняются дополнительным ограничениям за пределы тех, кто на других типах значений. Enums должен содержать только поля в качестве элементов (они даже не должны определять инициализаторы типа или конструкторов экземпляров); Они не должны реализовывать какие-либо интерфейсы; У них есть автоматическое расположение поля (§10.1.2); У них есть ровно один экземпляр поля, и он должен иметь базовый тип Enum; Все остальные поля должны быть статическими и буквальными (§16.1);

Так вот так, как они могут наследовать от System.enum, но имеют «базовый» тип - это единственное поле экземпляра, которым они могут иметь.

Существует также обсуждение по поведению бокса, но он не описывает явно Unboxing к базовому типу, что я вижу.

Раздел I, 8.5.2 заявляет, что enums - это «альтернативное имя для существующего типа», но «[F] или цели сопоставления подписей, Enum не является таким же, как базовый тип».

Разбиение II, 14.3 Изъязы: «Для всех других целей, включая проверку и выполнение кода, Uncoxed Enum свободно взаимозависимо от его базового типа. Enums можно всполнить в соответствующем типе экземпляра вполне, но этот тип не совпадает с коробкой Тип базового типа, поэтому бокс не теряет оригинальный тип enum ".

Разбиение III, 4.32 Объясняет поведение распаковки: «Тип тип значения содержащиеся в обман должно быть присвоение совместимо с тип значения. Отказ [Примечание. Это влияет на поведение с типовыми типами, см. Раздел II.14.3. Конец примечания] "

Что я намескаваю здесь, от Page 38 ЭКМА-335. (Я предлагаю вам скачать его просто иметь его):

CTS поддерживает Enum (также известное как тип перечисления), альтернативное имя для существующего типа. Для целей сопоставления подписей enum не является таким же, как основной тип. Однако экземпляры enum должны быть присвоенными - к базовому типу и наоборот. То есть без литья (см. § 8.3.3) или принуждение (см. § 8.3.2) требуется для преобразования от enum в базовый тип, а также не требуются от базового типа до enum. Enum значительно более ограничен, чем настоящий тип, следующим образом:

Базовый тип должен быть встроенным целочисленным типом. Enums должен получать от System.enum, следовательно, они являются типовыми типами. Как и все типы значений, они должны быть запечатаны (см. § 8.9.9).

enum Foo { Bar = 1 }
Foo x = Foo.Bar;

Это утверждение будет ложно из-за второго предложения:

x is int

Они являются То же самое (псевдонима), но их подпись не то же самое. Преобразование в и из int не актеры.

С стр. 46:

Основные типы - в перечислениях CTS являются альтернативными именами для существующих типов (§8.5.2), называемый их основным типом. За исключением подписи сопоставления (§8.5.2) перечисления рассматриваются как их базовый тип. Это подмножество - это набор типов хранения с удаленными перечислениями.

Вернитесь к моему Foo Enum раньше. Это утверждение будет работать:

Foo x = (Foo)5;

Если вы проверяете сгенерированный код моего основного метода в отражателе:

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 1
.locals init (
    [0] valuetype ConsoleTesting.Foo x)
L_0000: nop 
L_0001: ldc.i4.5 
L_0002: stloc.0 
L_0003: call string [mscorlib]System.Console::ReadLine()
L_0008: pop 
L_0009: ret 
}

Обратите внимание, что нет литых. ldc находится на стр. 86. Он загружает константу. i4 Найден на стр. 151, что указывает на тип 32-битное целое число. Там нет литых!

Извлеченный из MSDN:

В основном тип элементов по умолчанию элементы перечисления INT. По умолчанию первый перечислетель имеет значение 0, а значение каждого последовательного перечисления увеличивается на 1.

Итак, катушка возможен, но вам нужно заставить его:

Базовый тип указывает, сколько хранения выделяется для каждого перечисления. Однако для преобразования от типа Enum требуется явный актерский актерский лист в интегральный тип.

Когда вы коробка в свой enum в object, объект животных получен из System.Enum (реальный тип известен во время выполнения), так что это на самом деле int, Таким образом, актеры действительны.

  • (animal is Enum) возвращается true: По этой причине вы можете небровать животных в enum или событие в INT, выполняя явную кастинг.
  • (animal is int) возвращается false: То is Оператор (в чеке общего типа) не проверяет базовый тип для enums. Кроме того, по этой причине вам нужно сделать явное литье для преобразования Enum для Int.

Хотя типы Enum унаследованы от System.Enum, Любое преобразование между ними не является прямой, а бокс / распаковку один. От C # 3.0 Спецификация:

Тип перечисления - это отдельный тип с именованными константами. Каждый тип перечисления имеет базовый тип, который должен быть байт, sbyte, короткий, ushort, int, uint, long или ulong. Набор значений типа перечисления совпадает с набором значений нижележащего типа. Значения типа перечисления не ограничены значениями именованных констант. Типы перечислений определяются через декларации перечисления

Итак, пока ваш класс животных получен из System.Enum, это на самом деле int. Отказ Кстати, еще одна странная вещь System.Enum происходит от System.ValueType, Однако это все еще тип ссылки.

А. EnumБазовый тип - это тип, используемый для хранения значения констант. В вашем примере, даже если вы не четко определили значения, C # это:

enum Animal : int
{
    Dog = 0,
    Cat = 1
}

Внутренне, Animal состоит из двух постоянных с целочисленными ценностями 0 и 1. Вот почему вы можете явно отличить целое число для Animal и ан Animal целому числу. Если вы пройдете Animal.Dog к параметру, который принимает Animal, что вы действительно делаете, это передает 32-битное целочисленное значение Animal.Dog (В этом случае 0). Если вы даете Animal Новый базовый тип, то значения хранятся как этот тип.

Почему бы не ... Это совершенно действенно, например, для структуры, чтобы удерживать внутреннюю, и быть конвертируемым для INT с явным литым оператором ... Давайте смоделировать Enum:

interface IEnum { }

struct MyEnumS : IEnum
{
    private int inner;

    public static explicit operator int(MyEnumS val)
    {
        return val.inner;
    }

    public static explicit operator MyEnumS(int val)
    {
        MyEnumS result;
        result.inner = val;
        return result;
    }

    public static readonly MyEnumS EnumItem1 = (MyEnumS)0;
    public static readonly MyEnumS EnumItem2 = (MyEnumS)2;
    public static readonly MyEnumS EnumItem3 = (MyEnumS)10;

    public override string ToString()
    {
        return inner == 0 ? "EnumItem1" :
            inner == 2 ? "EnumItem2" :
            inner == 10 ? "EnumItem3" :
            inner.ToString();
    }
}

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

Давайте посмотрим на некоторое сравнение использования, с эквивалентным врачом:

enum MyEnum
{
    EnumItem1 = 0,
    EnumItem2 = 2,
    EnumItem3 = 10,
}

Сравнение использования:

Структура версии:

var val = MyEnum.EnumItem1;
val = (MyEnum)50;
val = 0;
object obj = val;
bool isE = obj is MyEnum;
Enum en = val;

Версия Enum:

var valS = MyEnumS.EnumItem1;
valS = (MyEnumS)50;
//valS = 0; // cannot simulate this
object objS = valS;
bool isS = objS is MyEnumS;
IEnum enS = valS;

Некоторые операции не могут быть смоделированы, но это все показывает, что я намеревался сказать ... Enums особенные, да ... Сколько особенного? не так много! знак равно

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