Явное преобразование int к enum и наоборот

518
28 августа 2017, 05:41
 enum Vehicles
{
    car,
    plane,
    ship,
    helicopter,
    truck = plane
}
static void Main()
{
    Console.WriteLine((Vehicles)1);//truck
    Console.WriteLine((Vehicles)2);//ship
    Console.WriteLine((Vehicles)100);//100
    Console.WriteLine((int)Vehicles.helicopter);// 3
    Console.ReadKey();
}

Я пытаюсь понять, почему, если мы явно приводим интовую переменную(или в нашем случае литерал) выводиться название переменной, которая хранит это значение, а в случае вывода литерала, которого нет в данном перечислении, выводиться сам литерал? И наоборот - если я привожу поле enum'a(если можно сказать "поле") к int, то выводиться его значение? То есть, при привидении int к Vehicles происходит поиск полей, которые хранят в себе значение этого int'a? Тогда почему в первом выводе truck, а не plane?

Answer 1

Все типы в CLR наследуют от System.Object в котором определен виртуальный метод строкового представления текущего объекта – ToString. Любой тип может переопределить этот метод, тем самым изменив представление по-умолчанию, которое определено в System.Object – вывод полного имени типа.

Например, тип Int32 (int) переопределяет этот метод для представления хранимого значения в виде форматированного, в зависимости от региональных настроек и формата, числа.

Каждый раз, когда вы определяете перечисление, вы создаете наследника класса System.Enum в котором, как вы уже догадались, тоже предопределен метод ToString для представления текущего значения перечисления в виде строки. Для вашего перечисления (Vehicles), будет сгенерирован такой IL

.class private auto ansi sealed Vehicles
       extends [mscorlib]System.Enum
{
  .field public specialname rtspecialname int32 value__
  .field public static literal valuetype Vehicles car = int32(0x00000000)
  .field public static literal valuetype Vehicles plane = int32(0x00000001)
  ...
  .field public static literal valuetype Vehicles truck = int32(0x00000001)
} // end of class Vehicles

Что соответствует примерно вот такому коду (переводчик из IL я так-себе, поэтому лучше перепроверить :)

sealed class Vehicles : System.Enum
{
    public const Vehicles car   = 0;
    public const Vehicles plane = 1;
    ...
    public const Vehicles truck = 1;
}

Теперь о том, как метод ToString который используется методом Console.WriteLine находит имя поля. В .NET есть специальный механизм, называемый рефлексия, при помощи которого можно получать информацию о произвольных типах (например, имена полей и их значений) при помощи этого механизма Enum просматривает список полей, и если есть совпадения по значению – то возвращает это имя. На практике за это отвечает этот код, который вызывается из метода Enum.ToString

private static String InternalFormat(RuntimeType eT, Object value)
{
    if (!eT.IsDefined(typeof(System.FlagsAttribute), false))
    {
        String retval = GetName(eT, value);
        if (retval == null)
            return value.ToString();
        else
            return retval;
        ...
    }

Из него хорошо видно, что если у перечисление нет атрибута Flags то метод должен просто вернуть имя поля или, в случае несовпадения, вернуть значение.

Тогда почему в первом выводе truck, а не plane

Как я говорил выше, что имена всех полей и значений выбираются при помощи рефлексии. Затем код сортирует значения полей и при помощи двоичного поиска ищет совпадение. Таким образом, поиск просто вернет первое совпадение. За это отвечает этот код из System.Enum

Array values = GetEnumRawConstantValues();
int index = BinarySearch(values, value);
if (index >= 0)
{
    string[] names = GetEnumNames();
    return names[index];
}

Можно попробовать манипулировать этим поиском, поместив одинаковые значения на границу отрезков:

enum Vehicles
{
    car         = 0,
    plane       = 1,
    cat         = 2,
    ship        = 2,
    helicopter  = 3,
    truck       = 4
}
...
string name = ((Vehicles) 2).ToString();
Console.WriteLine(name); // cat

Но всё таки лучше присваивать разные значения полям перечисления :)

READ ALSO
Непонятен код в Pipelines.RIO

Непонятен код в Pipelines.RIO

Ковыряю исходники SystemIO

298
Создание файлов в FileTable по UNC-пути к каталогу используя C# (CLR SQL) - C#

Создание файлов в FileTable по UNC-пути к каталогу используя C# (CLR SQL) - C#

Привет! Есть тестовая процедурка, написанная на C# CLR SQL, которая потом работает как обычная хранимкаПроцедура пытается создать файл в сетевой...

377
Десериализация JSON в C#

Десериализация JSON в C#

Есть класс в котором одно из свойств это объект из библиотеки dllСуть в том, что при вызове метода JsonConvert

433
Копирование картинки из тега img

Копирование картинки из тега img

Здравствуйте, есть капча при обновлении страницы появляеться новая

218