Як перетворити з System.Enum на базове ціле число?


93

Я хотів би створити загальний метод для перетворення будь-якого похідного System.Enum у відповідне ціле значення, без вставки та бажано без розбору рядка.

Наприклад, я хочу щось подібне:

// Trivial example, not actually what I'm doing.
class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        (int)anEnum;
    }
}

Але це, здається, не працює. Resharper повідомляє, що ви не можете передати вираз типу "System.Enum" типу "int".

Зараз я придумав це рішення, але я вважаю за краще щось ефективніше.

class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        return int.Parse(anEnum.ToString("d"));
    }
}

Будь-які пропозиції?


1
Я вважаю, що компілятор скаржиться, а не Resharper.
Кугель

1
Не обов'язково. У мене є метод розширення на System.Enum, і час від часу Resharper вирішує скаржитися: Неможливо перетворити аргумент екземпляра типу "Some.Cool.Type.That.Is.An.Enum" у "System.Enum", коли він безперечно є перерахунком. Якщо я компілюю і запускаю код, він працює чудово. Якщо я тоді вимкнув VS, продув кеш Resharper і запустив його назад, все буде гаразд, як тільки його буде виконано повторне сканування. Для мене це якась кеш-пам'ять. Може бути для нього однаково.
Мир

@Mir У мене ReSharper "скаржиться" і на це. Те саме виправлення для мене. Не знаю, чому це змішує ці типи, але це точно не компілятор.
akousmata

Відповіді:


134

Якщо ви не хочете грати,

Convert.ToInt32()

міг зробити трюк.

Прямий ролик (через (int)enumValue) неможливий. Зверніть увагу , що це також буде «небезпечним» , оскільки перерахування може мати різні типи , що лежать в основі ( int, long, byte...).

Більш формально: System.Enumне має прямих відносин успадкування з Int32(хоча обидва є ValueTypes), тому явний привід не може бути правильним у системі типів


2
Converter.ToInteger(MyEnum.MyEnumConstant);не дасть вам помилки тут. Будь ласка, відредагуйте цю частину.
nawfal

Дякую, @nawfal, ти маєш рацію. Я відредагував відповідь (і дізнався щось нове :) ...)
MartinStettner

Не працює, якщо основний тип відрізняється відint
Алекс Жуковський

@YaroslavSivakov це не спрацює, наприклад, для longперерахунків.
Олексій Жуковський

System.Enumє класом, тому це еталонний тип. Цінності, мабуть, поставлені в коробку.
Teejay

42

Я змусив його працювати, перекинувши об'єкт, а потім int:

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return (int)((object)enumValue);
    }
}

Це некрасиво і, мабуть, не найкращий спосіб. Я буду продовжувати возитися з цим, щоб перевірити, чи можу я придумати щось краще ....

EDIT: Якраз збирався опублікувати, що Convert.ToInt32 (enumValue) також працює, і помітив, що MartinStettner бив мене до цього.

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

Тест:

int x = DayOfWeek.Friday.ToInt();
Console.WriteLine(x); // results in 5 which is int value of Friday

EDIT 2: У коментарях хтось сказав, що це працює лише на C # 3.0. Я щойно тестував це в VS2005, як це, і він працював:

public static class Helpers
{
    public static int ToInt(Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

    static void Main(string[] args)
    {
        Console.WriteLine(Helpers.ToInt(DayOfWeek.Friday));
    }

Я дав вам +1, але це рішення буде працювати лише на C # 3.0 і вище.
Вадим

Я думаю, це задовольняє лише компілятор. Вам вдалося протестувати це під час виконання? Я не міг передати будь-яке значення цій функції ...
MartinStettner

Тому що це метод розширення? Або щось інше щодо перелічень у старих версіях C #?
BFree

Я додав тест до кінця мого допису. Я спробував це, і мені це вдалося.
BFree

@BFree: Схоже, це працює лише з методами розширення. Я спробував це з функцією "старого стилю" (в C # 2.0) і не встиг знайти синтаксис, щоб насправді передати йому якесь значення (саме про це був мій попередній коментар)
MartinStettner

14

Якщо вам потрібно перетворити будь-який перелік на його базовий тип (не всі перерахунки підтримуються int), ви можете використовувати:

return System.Convert.ChangeType(
    enumValue,
    Enum.GetUnderlyingType(enumValue.GetType()));

5
Це єдина куленепробивна відповідь.
Rudey 03.03.16

Чи це причина боксу розпакування?
b.ben

@ b.ben так. Convert.ChangeTypeприймає objectперший аргумент і повертається object.
Дрю Ноакс

Так, я насправді погоджуюся з @Rudey щодо цього, як би там не було, ви повинні робити тести на ефективність. Рішення BFree - це найшвидше. Приблизно у вісім разів швидше. У будь-якому випадку ми все ще говоримо про кліщів.
Двадцять

10

Чому потрібно винаходити колесо за допомогою хелперного методу? Цілком законно передавати enumзначення його базовому типу.

Це менше набирати текст, і на мою думку, більш читабельний, щоб використовувати ...

int x = (int)DayOfWeek.Tuesday;

... а не щось на зразок ...

int y = Converter.ToInteger(DayOfWeek.Tuesday);
// or
int z = DayOfWeek.Tuesday.ToInteger();

6
Я хочу перетворити будь-яке значення перерахунку. Тобто, у мене є різноманітні класи, які мають поля переліку, які обробляються певним кодом загальним чином. Ось чому простий склад для int не підходить.
orj

@orj, я не дуже впевнений, що ти маєш на увазі. Чи можете ви навести приклад, який показує необхідне вам використання?
ЛукаH

2
Іноді ви не можете безпосередньо подати перерахування, наприклад, у випадку із загальним методом. Оскільки загальні методи не можуть бути обмежені перерахунками, ви обмежили це чимось на зразок структура, яка не може бути передана в int. У цьому випадку ви можете використовувати Convert.ToInt32 (enum) для цього.
Даніель Т. 02

Мені подобається думка, що метод розширення ToInteger () дотримується того самого формату, що і ToString (). Я думаю, що менш читати, щоб передати to int з одного боку, коли ви хочете значення int та використовувати ToString (), коли нам потрібне значення рядка, особливо коли код є поруч.
Луїза Егглтон,

Крім того, використання методу розширення може додати послідовності вашій основі коду. Оскільки, як зазначив @nawfal, існує декілька способів отримати значення int з переліку, створюючи метод розширення та застосовуючи його використання, означає, що кожен раз, коли ви отримуєте значення int перечислення, ви використовуєте той самий метод
Луїза Егглтон,

4

З моєї відповіді тут :

Дано eяк у:

Enum e = Question.Role;

Тоді такі роботи:

int i = Convert.ToInt32(e);
int i = (int)(object)e;
int i = (int)Enum.Parse(e.GetType(), e.ToString());
int i = (int)Enum.ToObject(e.GetType(), e);

Останні два - просто потворні. Перший повинен бути більш читабельним, хоча другий набагато швидший. Або, можливо, метод розширення є найкращим, найкращим з обох світів.

public static int GetIntValue(this Enum e)
{
    return e.GetValue<int>();
}

public static T GetValue<T>(this Enum e) where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
{
    return (T)(object)e;
}

Тепер ви можете зателефонувати:

e.GetValue<int>(); //or
e.GetIntValue();

Ви впевнені, що другий швидший? Я припускаю, що він зафіксує перелік, а потім розпакує назад до int?
yoyo

1
@yoyo у eзмінної вже є поміщений екземпляр фактичного типу значення, тобто enum. Коли ви пишете, Enum e = Question.Role;це вже в коробці. Питання полягає в System.Enumтому, щоб перетворити поле в коробку назад на базовий тип int (розпакування). Отже, тут розпакування стосується лише продуктивності. (int)(object)eє прямим викликом розпаковування; так, це має бути швидше, ніж інші підходи. Дивіться це
nawfal

дякую за пояснення. У мене склалося помилкове враження, що екземпляр System.Enum є невкладеним значенням. Дивіться також це - msdn.microsoft.com/en-us/library/aa691158(v=vs.71).aspx
yoyo

@yoyo хмм так. Відповідна цитата за посиланням: Від будь-якого типу переліку до типу System.Enum.
nawfal

1

Кастинг з a System.Enumна intмене прекрасно працює (це також на MSDN ). Можливо, це помилка Resharper.


1
Яку версію виконання ви використовуєте?
JoshBerke

2
посилання показує кастинг підтипів System.Enum, а не System.Enumсебе.
nawfal

Подібною проблемою у мене була Snafu кешу Resharper. Закриття VS та видалення кешу Resharper призвели до того, що помилка зникла навіть після повного повторного сканування.
Мир

1

Оскільки Enums обмежений байтом, sbyte, коротким, ушортом, int, uint, long і ulong, ми можемо зробити деякі припущення.

Ми можемо уникати винятків під час перетворення, використовуючи найбільший доступний контейнер. На жаль, який контейнер використовувати, не зрозуміло, оскільки улонг вибухне для від'ємних чисел, а довгий - для номерів між long.MaxValue та ulong.MaxValue. Нам потрібно переключатися між цими варіантами, виходячи з основного типу.

Звичайно, вам все одно потрібно вирішити, що робити, коли результат не вміщується всередині int. Я думаю, що кастинг - це нормально, але все ж є деякі проблеми:

  1. для переліків на основі типу з просторовим полем, більшим за int (довге та довге), можливо, деякі перерахунки оцінюватимуть за однаковим значенням.
  2. додаючи число, яке перевищує int.MaxValue, видасть виняток, якщо ви знаходитесь у контрольованому регіоні.

Ось моя пропозиція, я залишу читачеві вирішити, де виставити цю функцію; як помічник чи продовження.

public int ToInt(Enum e)
{
  unchecked
  {
    if (e.GetTypeCode() == TypeCode.UInt64)
      return (int)Convert.ToUInt64(e);
    else
      return (int)Convert.ToInt64(e);
  }
}

-1

Не забувайте, що сам тип Enum має в ньому купу статичних допоміжних функцій. Якщо все, що ви хочете зробити, це перетворити екземпляр enum у відповідний йому цілий тип, тоді кастинг, мабуть, є найбільш ефективним способом.

Я думаю, що ReSharper скаржиться, тому що Enum - це не перерахування якогось конкретного типу, а самі перерахування походять від скалярного оцінного типу, а не Enum. Якщо вам потрібен адаптивний кастинг у загальному вигляді, я б сказав, що це може вам добре підійти (зауважте, що сам тип перерахування також включений до загального:

public static EnumHelpers
{
    public static T Convert<T, E>(E enumValue)
    {
        return (T)enumValue;
    }
}

Потім це можна використовувати так:

public enum StopLight: int
{
    Red = 1,
    Yellow = 2,
    Green = 3
}

// ...

int myStoplightColor = EnumHelpers.Convert<int, StopLight>(StopLight.Red);

Я не можу сказати напевно вгорі голови, але вищезгаданий код може бути навіть підтриманий виводом типу C #, що дозволяє наступне:

int myStoplightColor = EnumHelpers.Convert<int>(StopLight.Red);

1
На жаль, у вашому прикладі Е може бути будь-якого типу. Загальні параметри не дозволяють мені використовувати Enum як обмеження параметрів типу. Я не можу сказати: де Т: Енум
Вадим

Однак ви можете використовувати де T: struct. Це не настільки суворо, як ви хочете, але це вирізало б будь-який тип посилань (що, на мою думку, саме те, що ви намагаєтесь зробити)
jrista

Ви можете використовувати: if (! typeof (E) .IsEnum) киньте новий ArgumentException ("E повинен бути переліченим типом");
діадіора

1
Це навіть не віддалено, статичний помічник навіть не дійсний.
Ніколі

1
Чому хто - то повинен використовувати EnumHelpers.Convert<int, StopLight>(StopLight.Red);замість (int)StopLight.Read;? Також питання йдеться про System.Enum.
nawfal
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.