Как преобразовать из System.Enum в базовое целое число?

Я хотел бы создать универсальный метод для преобразования любого производного типа 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"));
    }
}

Какие-либо предложения?


person orj    schedule 26.05.2009    source источник
comment
Я считаю, что жалуется компилятор, а не Resharper.   -  person Kugel    schedule 28.08.2012
comment
Не обязательно. У меня есть метод расширения в System.Enum, и иногда Resharper решает пожаловаться: не удается преобразовать тип аргумента экземпляра Some.Cool.Type.That.Is.An.Enum в System.Enum, когда он, несомненно, является перечислением. Если я компилирую и запускаю код, он работает нормально. Если я затем закрою VS, сдулю кеш Resharper и снова запустю его, все будет в порядке, как только будет выполнено повторное сканирование. Для меня это какой-то сбой с кешем. Может быть и с ним.   -  person Mir    schedule 04.03.2014
comment
@Mir Я тоже жаловался на ReSharper. То же самое для меня. Не уверен, почему он смешивает эти типы, но это определенно не компилятор.   -  person akousmata    schedule 27.10.2015


Ответы (9)


Если ты не хочешь кастовать,

Convert.ToInt32()

мог сделать свое дело.

Прямая трансляция (через (int)enumValue) невозможна. Обратите внимание, что это также было бы «опасно», поскольку перечисление может иметь разные базовые типы (int, long, _5 _...).

Более формально: System.Enum не имеет прямого отношения наследования с Int32 (хотя оба являются ValueTypes), поэтому явное приведение не может быть правильным в системе типов

person MartinStettner    schedule 26.05.2009
comment
Converter.ToInteger(MyEnum.MyEnumConstant); здесь не выдаст ошибки. Пожалуйста, отредактируйте эту часть. - person nawfal; 01.12.2013
comment
Спасибо, @nawfal, ты прав. Я отредактировал ответ (и узнал что-то новое :) ...) - person MartinStettner; 01.12.2013
comment
Не работает, если базовый тип отличается от int - person Alex Zhukovskiy; 23.12.2016
comment
@YaroslavSivakov это не сработает, например для long перечисления. - person Alex Zhukovskiy; 28.06.2018
comment
System.Enum - это класс, поэтому это ссылочный тип. Значения, вероятно, заключены в рамку. - person Teejay; 20.09.2018

Я заставил его работать, приведя к объекту, а затем к int:

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

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

РЕДАКТИРОВАТЬ: как раз собирался опубликовать, что 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

РЕДАКТИРОВАТЬ 2: В комментариях кто-то сказал, что это работает только в С # 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));
    }
person BFree    schedule 26.05.2009
comment
Я дал вам +1, но это решение будет работать только с C # 3.0 и выше. - person Vadim; 26.05.2009
comment
Думаю, это устраивает только компилятор. Удалось ли вам протестировать это во время выполнения? Я не мог передать значение этой функции ... - person MartinStettner; 26.05.2009
comment
Потому что это метод расширения? Или что-то другое в перечислениях в старых версиях C #? - person BFree; 26.05.2009
comment
Добавил тест в конец поста. Я попробовал это, и у меня это сработало. - person BFree; 26.05.2009
comment
@BFree: похоже, работает только с методами расширения. Я пробовал это с помощью функции старого стиля (в C # 2.0) и не смог найти синтаксис, чтобы фактически передать ей какое-либо значение (это то, о чем был мой предыдущий комментарий) - person MartinStettner; 26.05.2009
comment
Если вам нужна реальная проблема, попробуйте пойти в противоположном направлении, от параметра общего типа TEnum к int. Удачи! - person Triynko; 16.05.2018

Если вам нужно преобразовать любое перечисление в его базовый тип (не все перечисления поддерживаются int), вы можете использовать:

return System.Convert.ChangeType(
    enumValue,
    Enum.GetUnderlyingType(enumValue.GetType()));
person Drew Noakes    schedule 09.11.2015

Зачем нужно изобретать велосипед с помощью вспомогательного метода? Совершенно законно привести значение enum к его базовому типу.

Он меньше печатает и, на мой взгляд, более читается в использовании ...

int x = (int)DayOfWeek.Tuesday;

... а не что-то вроде ...

int y = Converter.ToInteger(DayOfWeek.Tuesday);
// or
int z = DayOfWeek.Tuesday.ToInteger();
person LukeH    schedule 26.05.2009
comment
Я хочу преобразовать ЛЮБОЕ значение перечисления. Т.е. у меня есть множество классов, у которых есть поля перечисления, которые обрабатываются некоторым кодом обычным образом. Вот почему простое приведение к int не подходит. - person orj; 27.05.2009
comment
@orj, я не совсем понимаю, что ты имеешь в виду. Можете ли вы привести пример, показывающий, какое использование вам нужно? - person LukeH; 27.05.2009
comment
Иногда вы не можете напрямую привести перечисление, например, в случае универсального метода. Поскольку общие методы не могут быть ограничены перечислениями, вы должны ограничить их чем-то вроде структуры, которая не может быть преобразована в int. В этом случае вы можете использовать Convert.ToInt32 (enum) для этого. - person Daniel T.; 02.02.2010
comment
Мне нравится идея, что метод расширения ToInteger () следует тому же формату, что и ToString (). Я думаю, что, с одной стороны, менее читабельно приводить к int, когда вам нужно значение int, и использовать ToString (), когда нам нужно строковое значение, особенно когда код находится рядом. - person Louise Eggleton; 17.01.2018
comment
Кроме того, использование метода расширения может повысить согласованность вашей базы кода. Поскольку, как указал @nawfal, существует несколько способов получить значение int из перечисления, создание метода расширения и обеспечение его использования означает, что каждый раз, когда вы получаете значение int перечисления, вы используете тот же метод - person Louise Eggleton; 17.01.2018

Из моего ответа здесь:

Учитывая 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();
person nawfal    schedule 01.12.2013
comment
Вы уверены, что второй быстрее? Я предполагаю, что он упакует перечисление, а затем распакует обратно в int? - person yoyo; 13.07.2014
comment
@yoyo переменная e уже имеет упакованный экземпляр фактического типа значения, то есть enum. Когда вы пишете Enum e = Question.Role;, он уже упакован. Вопрос в том, чтобы преобразовать упакованный System.Enum обратно в базовый тип int (распаковка). Итак, распаковка - это только проблема производительности. (int)(object)e - прямой вызов распаковки; да должно быть быстрее, чем другие подходы. См. это - person nawfal; 13.07.2014
comment
Спасибо за объяснение. У меня сложилось ошибочное впечатление, что экземпляр System.Enum был распакованным значением. См. Это тоже - msdn.microsoft.com/ en-us / library / aa691158 (v = vs.71) .aspx - person yoyo; 13.07.2014
comment
@yoyo хм, да. Соответствующая цитата из ссылки: От любого типа перечисления к типу System.Enum. - person nawfal; 13.07.2014


Поскольку перечисления ограничены байтами, sbyte, short, ushort, int, uint, long и ulong, мы можем сделать некоторые предположения.

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

Конечно, вам все равно нужно решить, что делать, если результат не помещается в int. Я думаю, что кастинг - это нормально, но все же есть некоторые подводные камни:

  1. для перечислений, основанных на типе с полем, большим, чем int (long и ulong), возможно, некоторые перечисления будут оценивать одно и то же значение.
  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);
  }
}
person eisenpony    schedule 12.03.2018

Если вы прочитаете код для Convert.ToInt32, вы увидите, что он преобразует Enum в IConvertible, а затем вызывает ToInt32(null).

person Dwedit    schedule 08.01.2021

Не забывайте, что в самом типе 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);
person jrista    schedule 26.05.2009
comment
К сожалению, в вашем примере E может быть любого типа. Обобщения не позволяют мне использовать Enum в качестве ограничения параметра типа. Не могу сказать: где T: Enum - person Vadim; 26.05.2009
comment
Однако вы можете использовать where T: struct. Это не так строго, как вы хотите, но он вырезал бы любой ссылочный тип (что, я думаю, это то, что вы пытаетесь сделать.) - person jrista; 26.05.2009
comment
вы можете использовать: if (! typeof (E) .IsEnum) throw new ArgumentException (E должен быть перечислимым типом); - person diadiora; 03.07.2010
comment
Это даже отдаленно неверно, статический помощник даже не действителен. - person Nicholi; 29.06.2013
comment
Почему кто-то должен использовать EnumHelpers.Convert<int, StopLight>(StopLight.Red); вместо (int)StopLight.Read;? Также вопрос касается System.Enum. - person nawfal; 01.12.2013