Как преобразовать System.Enum в базовое целое число?
-
05-09-2019 - |
Вопрос
Я хотел бы создать универсальный метод для преобразования любого производного типа 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"));
}
}
Какие-либо предложения?
Решение
Если вы не хотите проводить кастинг,
Convert.ToInt32()
мог бы сделать свое дело.
Прямой состав (через (int)enumValue
) это невозможно.Обратите внимание, что это также было бы «опасно», поскольку перечисление может иметь разные базовые типы (int
, long
, byte
...).
Более формально: System.Enum
не имеет прямых отношений наследования с Int32
(хотя оба ValueType
s), поэтому явное приведение не может быть правильным в рамках системы типов.
Другие советы
Я заставил его работать, приведя его к объекту, а затем к int:
public static class EnumExtensions
{
public static int ToInt(this Enum enumValue)
{
return (int)((object)enumValue);
}
}
Это некрасиво и, вероятно, не лучший способ.Я буду продолжать возиться с этим, чтобы посмотреть, смогу ли я придумать что-нибудь получше....
РЕДАКТИРОВАТЬ:Как раз собирался опубликовать, что Convert.ToInt32(enumValue) тоже работает, и заметил, что МартинСтеттнер опередил меня в этом.
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:В комментариях кто-то сказал, что это работает только в 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));
}
Если вам нужно конвертировать любой enum к его базовому типу (не все перечисления поддерживаются int
), то вы можете использовать:
return System.Convert.ChangeType(
enumValue,
Enum.GetUnderlyingType(enumValue.GetType()));
Зачем нужно изобретать велосипед с помощью вспомогательного метода?Совершенно законно разыгрывать enum
значение к его базовому типу.
Это требует меньше набора текста и, на мой взгляд, более удобочитаемо в использовании...
int x = (int)DayOfWeek.Tuesday;
...а не что-то вроде...
int y = Converter.ToInteger(DayOfWeek.Tuesday);
// or
int z = DayOfWeek.Tuesday.ToInteger();
Из моего ответа здесь:
Данный 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();
Кастинг из System.Enum
для int
у меня работает нормально (это также есть на MSDN).Возможно, это ошибка Resharper.
Поскольку Enums ограничены byte, sbyte, short, ushort, int, uint, long и ulong, мы можем сделать некоторые предположения.
Мы можем избежать исключений во время преобразования, используя самый большой доступный контейнер.К сожалению, неясно, какой контейнер использовать, поскольку ulong будет работать для отрицательных чисел, а long — для чисел между long.MaxValue и ulong.MaxValue.Нам нужно переключаться между этими вариантами в зависимости от базового типа.
Конечно, вам все равно нужно решить, что делать, если результат не помещается внутри целого числа.Я думаю, что с кастингом все в порядке, но есть еще некоторые ошибки:
- для перечислений, основанных на типе с пространством полей больше, чем int (long и ulong), возможно, некоторые перечисления будут иметь одно и то же значение.
- приведение числа большего, чем int.MaxValue, вызовет исключение, если вы находитесь в проверенном регионе.
Вот мое предложение: я оставлю читателю самому решать, где разместить эту функцию;в качестве помощника или расширения.
public int ToInt(Enum e)
{
unchecked
{
if (e.GetTypeCode() == TypeCode.UInt64)
return (int)Convert.ToUInt64(e);
else
return (int)Convert.ToInt64(e);
}
}
Не забывайте, что сам тип 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);