Введение: почему enum без int — как кофе без кофеина?
Представьте ситуацию: вы пишете крутой код на C#, используете перечисления (enum) для хранения статусов заказа, дней недели или уровней доступа. Всё красиво и читаемо. Но вдруг приходит задача: сохранить это значение в базу данных, отправить по HTTP API или просто вывести в лог. И тут вы упираетесь в стену — enum нужно превратить в int.
Это не просто «хотелка» из учебника. По статистике Stack Overflow, вопрос «Get int value from enum in C#» собирает сотни тысяч просмотров. Даже опытные разработчики иногда путаются: кастинг? Convert.ToInt32? А что если тип базового целого числа — не int?
В этой статье мы разберём все легальные способы получить целочисленное значение из enum в C#. Начиная от простого приведения типов и заканчивая продвинутыми трюками с Unsafe.As (да, такое тоже есть!). Вы узнаете, какой метод быстрее, какой безопаснее, а какой лучше не использовать в продакшене.
1. Как устроен enum в C#: краткий ликбез
Прежде чем конвертировать, давайте вспомним матчасть. В C# enum — это тип-значение, который наследуется от System.Enum, а его базовый тип (underlying type) по умолчанию — int. Но вы можете явно указать другой тип:
public enum Status : byte
{
New = 0,
Active = 1,
Closed = 2
}
public enum Priority : long
{
Low = 10,
Medium = 50,
High = 100
}
Это важно! Если ваш enum использует byte или long, то простое приведение к int может привести к ошибке или потере данных.
2. Способ №1: явное приведение (cast) — классика жанра
Самый очевидный и быстрый способ — использовать оператор приведения типа. Работает, когда вы точно знаете, что базовый тип — int.
enum Day { Monday = 1, Tuesday = 2, Wednesday = 3 }
Day today = Day.Tuesday;
int dayValue = (int)today; // Результат: 2
Console.WriteLine(dayValue); // Выведет: 2
Плюсы:
- Максимальная производительность — компилятор генерирует одну IL-инструкцию.
- Простота и читаемость.
Минусы:
- Если базовый тип не
int, компилятор выдаст ошибку или предупреждение. - Не работает с
enum, определёнными черезlongилиbyte(требуется двойной каст).
Когда использовать: в 90% случаев, когда ваш enum стандартный (: int).
3. Способ №2: Convert.ToInt32 — универсальный солдат
Метод Convert.ToInt32() — это швейцарский нож для конвертации. Он принимает object и пытается привести его к int через интерфейс IConvertible.
enum Status { New = 0, Active = 1, Closed = 2 }
Status current = Status.Active;
int statusCode = Convert.ToInt32(current); // Результат: 1
Console.WriteLine(statusCode); // 1
Плюсы:
- Работает с любым базовым типом (
byte,short,longи т.д.). - Не требует явного знания типа enum.
Минусы:
- Медленнее, чем прямой каст (вызов виртуального метода).
- Может выбросить
InvalidCastException, если передать не enum.
Когда использовать: когда вы пишете обобщённый код или работаете с enum, базовый тип которого неизвестен на этапе компиляции.
4. Способ №3: GetHashCode() — опасный трюк (не делайте так!)
В интернете можно встретить «гениальный» совет: использовать GetHashCode(). Да, технически он вернёт целое число. Но это плохая практика.
enum Color { Red = 10, Green = 20, Blue = 30 }
Color c = Color.Green;
int hash = c.GetHashCode(); // Вернёт 20? Или нет?
Console.WriteLine(hash); // Скорее всего 20, но гарантий нет!
Почему это плохо? Документация Microsoft не гарантирует, что GetHashCode() вернёт именно значение enum. Это может быть результат хэш-функции, особенно для enum с long базовым типом.
Вердикт: никогда не используйте GetHashCode() для получения числового значения enum. Это баг, который вы будете искать неделями.
5. Способ №4: Работа с Enum.GetUnderlyingType — для гиков
Если вы пишете библиотеку или инструмент, который должен работать с любым enum, используйте рефлексию и Enum.GetUnderlyingType().
public static int EnumToInt(Enum value)
{
// Получаем базовый тип
Type underlyingType = Enum.GetUnderlyingType(value.GetType());
// Конвертируем через Convert
return Convert.ToInt32(Convert.ChangeType(value, underlyingType));
}
// Пример использования
enum Permission : byte { Read = 1, Write = 2, Execute = 4 }
Permission p = Permission.Write;
int result = EnumToInt(p); // Результат: 2
Плюсы:
- Абсолютная универсальность — работает для
byte,short,long. - Безопасность типов.
Минусы:
- Медленно (рефлексия + двойная конвертация).
- Избыточно для 99% задач.
6. Способ №5: unsafe и Unsafe.As — для экстремалов (C# 7.3+)
Если вам нужна максимальная производительность в критическом коде (игры, high-load сервисы), можно использовать System.Runtime.CompilerServices.Unsafe. Этот метод переинтерпретирует память enum как int без каких-либо проверок.
using System.Runtime.CompilerServices;
enum BigEnum : long
{
Min = long.MinValue,
Max = long.MaxValue
}
BigEnum val = BigEnum.Max;
long rawValue = Unsafe.As<BigEnum, long>(ref val); // Результат: 9223372036854775807
Console.WriteLine(rawValue);
Плюсы:
- Максимальная скорость (н