1. Введение
В программировании часто приходится оперировать списками заранее известных, ограниченных вариантов. Например: дни недели, цвета светофора, уровни сложности игры, статусы заказа. Каждый из этих вариантов обычно представлен фиксированным значением. Вот тут мы и подбираемся ближе к нашей сегодняшней теме — перечислениям (enum).
Перечисление — это специальный тип данных, который позволяет объявлять набор именованных констант.
enum — как коробка с разложенными по ячейкам ярлыками, где каждый ярлык — уникальное имя, связанное с числом.
- Ваш код становится читаемым и самодокументируемым;
- Вместо загадочных «магических чисел» вроде 0, 1, 2 появляются выразительные имена: DayOfWeek.Monday или TrafficLight.Red;
- Компилятор будет ругаться, если вы попытаетесь присвоить некорректное значение;
- За каждым элементом скрывается… обычное целое число! (Но к этому мы еще вернемся.)
2. Синтаксис объявления перечисления
Давайте создадим первое перечисление — типы погоды для нашего будущего приложения. Вот как это будет выглядеть:
// Перечисление погодных условий
public enum WeatherCondition
{
Sunny, // 0
Cloudy, // 1
Rainy, // 2
Stormy, // 3
Snowy // 4
}
- Ключевое слово enum определяет новый тип.
- В фигурных скобках — имена вариантов. По умолчанию каждому варианту ставится в соответствие целое число, начиная с нуля (Sunny == 0, Cloudy == 1 и т.д.).
- Можно явно задать значения:
public enum WeatherCondition
{
Sunny = 1,
Cloudy = 2,
Rainy = 4,
Stormy = 8,
Snowy = 16
}
Это удобно, если значения должны соответствовать каким-то внешним стандартам или протоколам.
3. Использование перечислений
Представим, что мы написали приложение — небольшую консольную программу, которая реагирует на погоду.
// Класс приложения
class Program
{
static void Main()
{
// Используем наше перечисление для хранения состояния погоды
WeatherCondition todayWeather = WeatherCondition.Sunny;
// Выведем на экран
Console.WriteLine($"Сегодня погода: {todayWeather} ({(int)todayWeather})");
// А теперь сымитируем смену погоды
todayWeather = WeatherCondition.Rainy;
Console.WriteLine($"Ой! Погода изменилась: {todayWeather} ({(int)todayWeather})");
}
}
Что произойдет?
Сегодня погода: Sunny (0)
Ой! Погода изменилась: Rainy (2)
- todayWeather — это не строка, не число, а элемент перечисления.
- Приведение к (int) позволяет увидеть «числовой» код данного значения.
Кстати, перечисления отлично сочетаются с условиями:
if (todayWeather == WeatherCondition.Rainy)
Console.WriteLine("Возьмите зонт!");
4. Практические сценарии использования enum
Где встречаются перечисления?
- В бизнес-приложениях: статусы заказов (New, Processing, Shipped, Cancelled).
- В системах управления пользователями: уровни доступа (User, Moderator, Admin).
- В играх: состояния персонажа (Idle, Moving, Jumping, Falling).
- В GUI: кнопки, сообщения, цвета иконок.
А если углубляться в .NET — вы их найдете практически в каждом втором классе стандартной библиотеки! Например, у файловых операций есть перечисления для указания режима открытия файла (чтение, запись, создание).
5. Явные значения и диапазон значений
Иногда хочется преобразовать набор int значений, используемых в коде, в enum. Это сделать очень просто:
public enum CompassDirection
{
North = 10,
East = 20,
South = 30,
West = 40
}
Теперь CompassDirection.North = 10, East = 20, и т. д. Если какой-то элемент не указан явно, он автоматически получает значение +1 относительно предыдущего.
Тип хранения
По умолчанию enum хранится как int (4-байтовое целое число). Но вы можете указать свой базовый тип, если хотите сэкономить память:
public enum TinyEnum : byte
{
First, // 0
Second, // 1
Third // 2
}
Теперь каждое значение перечисления — всего один байт!
Полный список возможных типов: byte, sbyte, short, ushort, int, uint, long, ulong.
6. Преобразование между enum и числами/строками
Преобразование enum ↔ int:
Чтобы преобразовать enum в int и обратно, вы можете просто использовать typecast:
WeatherCondition current = WeatherCondition.Cloudy;
int number = (int)current; // Получим 1
WeatherCondition fromNumber = (WeatherCondition)2; // Получим Rainy
Преобразование enum ↔ string:
Преобразовать enum в строку легко: достаточно вызвать метод ToString().
// Преобразовываем enum в строку
string name = WeatherCondition.Stormy.ToString(); // "Stormy"
// Преобразовываем строку в enum с помощью Enum.Parse
WeatherCondition parsed = (WeatherCondition)Enum.Parse(typeof(WeatherCondition), "Stormy");
// Также можно использовать Enum.TryParse
WeatherCondition parsedOk;
if (Enum.TryParse("Snowy", out parsedOk))
{
Console.WriteLine(parsedOk); // Snowy
}
Типичная ошибка:
Если вы приведёте число к enum, для которого не определено такого члена, компилятор не ругнется, но вы получите "виртуальное" состояние.
Например:
WeatherCondition weird = (WeatherCondition)999;
Console.WriteLine(weird); // 999 (оно не превратится в "Unknown"!)
То есть, программисту нужно быть внимательным при подобных преобразованиях.
7. switch и enum созданы друг для друга
Перечисления (enum) отлично сочетаются с оператором switch. Это удобный и наглядный способ “реагировать” на разные значения перечисления — без длинных “if-else”.
Пример: реакция на погоду
public enum WeatherCondition
{
Sunny,
Cloudy,
Rainy,
Stormy,
Snowy
}
WeatherCondition today = WeatherCondition.Rainy;
switch (today)
{
case WeatherCondition.Sunny:
Console.WriteLine("Солнечно! Можно идти гулять.");
break;
case WeatherCondition.Cloudy:
Console.WriteLine("Пасмурно. Можно взять кофту.");
break;
case WeatherCondition.Rainy:
Console.WriteLine("Идет дождь. Не забудьте зонт!");
break;
case WeatherCondition.Stormy:
Console.WriteLine("Шторм! Лучше остаться дома.");
break;
case WeatherCondition.Snowy:
Console.WriteLine("Снег идёт — надевайте шапку!");
break;
default:
Console.WriteLine("Неизвестная погода...");
break;
}
- Каждый случай (case) соответствует одному значению перечисления.
- Если значение не совпадает ни с одним из известных вариантов, выполняется default (это полезно, если вдруг в переменную попало что-то неожиданное).
Чем хороша связка enum + switch?
- Код читабелен: все возможные варианты сразу видны по списку case.
- Удобно расширять: добавили новый тип погоды — просто добавьте ещё один case.
- Безопасность: компилятор предупредит, если вы забыли обработать все варианты (начиная с C# 8.0 можно даже включить проверку на исчерпывающий список).
Пример: enum с логикой для игры
public enum GameState
{
Start,
Playing,
Paused,
GameOver
}
void PrintState(GameState state)
{
switch (state)
{
case GameState.Start:
Console.WriteLine("Добро пожаловать в игру!");
break;
case GameState.Playing:
Console.WriteLine("Игра идёт. Удачи!");
break;
case GameState.Paused:
Console.WriteLine("Пауза. Можно передохнуть.");
break;
case GameState.GameOver:
Console.WriteLine("Игра окончена. Попробуйте снова!");
break;
default:
Console.WriteLine("Неизвестное состояние игры.");
break;
}
}
Советы:
- Если у вас много похожих действий для нескольких вариантов, можно объединять их:
switch (today)
{
case WeatherCondition.Rainy:
case WeatherCondition.Stormy:
Console.WriteLine("Не забудьте зонт и одежду потеплее!");
break;
}
- Не забывайте про default, чтобы обработать неожиданные значения (например, если кто-то привёл число к enum напрямую).
- Со switch удобно строить “машины состояний”, меню и любые системы с фиксированным набором вариантов.
8. Перечисления и основные методы/сервисы .NET
.NET предоставляет кучу полезных методов для перечислений:
- Enum.GetNames(typeof(WeatherCondition)) — получить все имена.
- Enum.GetValues(typeof(WeatherCondition)) — все значения.
- Enum.IsDefined(typeof(WeatherCondition), "Rainy") — узнать, есть ли в перечислении такой член.
- Документация: System.Enum
Пример вывода всех вариантов погоды:
foreach (var name in Enum.GetNames(typeof(WeatherCondition)))
{
Console.WriteLine(name);
}
9. Частые ошибки и «подводные камни»
- Используют "магические числа" вместо перечислений, теряя читаемость.
- Не обрабатывают значения, которые не определены в перечислении (см. пункт с приведением числа).
- Называют элементы перечисления не по смыслу (Value1, Value2... — и уже никто не помнит, что это значит).
- Нарушают принцип единственной ответственности — используют одно перечисление для разнородных вещей.
Лучшее решение: всегда использовать перечисления, когда есть конечный набор вариантов, и давать элементам смысловые, самодокументируемые имена.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ