Перечисления enum C#

Про перечисления (enum) в C# мы пока только знаем, что они есть и, согласно принятой классификации типов данных C# enum относится к типам значений или значимым типам (тут кому как удобнее называть). Сегодня мы попробуем разобраться с тем, как определяются и используются перечисления enum в C#, какие операции можно выполнять с перечислениями и напишем небольшой пример, демонстрирующий использование перечислений в C#.

Как определить enum в проекте C#

Перечисление (enum) — это пользовательский тип данных, представляющий собой набор именованных целочисленных констант. Чтобы объявить перечисление enum в своем приложении необходимо использовать ключевое слово enum и придерживаться следующего правила:

enum Название_типа : целочисленный_тип
{
    имена_констант_через_запятую
}

Вначале идет ключевое слово enum, которое указывает на то, что тип данных относится к перечислениям. Далее идет Название_типа — это может быть любая строка допустимая для использования в именах переменных C#. После названия через двоеточие указывается тип данных для констант в перечислении, если тип не указан, то по умолчанию используется тип int. И, наконец, в фигурных скобках через запятую указываются имена констант для перечисления. Например, ниже представлено перечисление, содержащее дни недели:

enum Days
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}

Значения констант в enum

В самом начале было сказано, что enum — это, по сути, набор именованных целочисленных констант. По умолчанию связанные значения констант элементов перечисления имеют тип int. Они начинаются с нуля и увеличиваются на единицу в соответствии с порядком текста определения. Чтобы убедиться в этом, достаточно написать, например, вот такой код:

Console.WriteLine($"{nameof(Days.Monday)} = {(int)Days.Monday}");
Console.WriteLine($"{nameof(Days.Tuesday)} = {(int)Days.Tuesday}");
Console.WriteLine($"{nameof(Days.Wednesday)} = {(int)Days.Wednesday}");
Console.WriteLine($"{nameof(Days.Thursday)} = {(int)Days.Thursday}");
Console.WriteLine($"{nameof(Days.Friday)} = {(int)Days.Friday}");
Console.WriteLine($"{nameof(Days.Saturday)} = {(int)Days.Saturday}");
Console.WriteLine($"{nameof(Days.Sunday)} = {(int)Days.Sunday}");

enum Days
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}
nameof(...) — это выражение, с помощью которого можно получить имя переменной в виде строки.

и увидеть в консоли следующее:

Monday = 0
Tuesday = 1
Wednesday = 2
Thursday = 3
Friday = 4
Saturday = 5
Sunday = 6

При этом, мы можем изменить значения констант как нам угодно (но только в рамках используемого для констант типа данных). Например, мы можем задать вот такие значения для наших констант:

 enum Days {
    Monday = 5,
    Tuesday = 7,
    Wednesday = 10,
    Thursday = 18,
    Friday = 25,
    Saturday = 48,
    Sunday = 64
}

Можем указать свой собственный тип данных для констант:

enum Days : short
{
    Monday = 1,
    Tuesday = 2,
    Wednesday = 3,
    Thursday = 5,
    Friday = 5,
    Saturday = -1,
    Sunday = -2
}

При этом, если мы попробуем определить вот такой тип перечисления:

enum Days : byte
{
    Monday = 1,
    Tuesday = 2,
    Wednesday = 3,
    Thursday = 5,
    Friday = 5,
    Saturday = 10,
    Sunday = -1
}

то компилятор C# выдаст нам ошибку:

Ошибка CS0031 Значение константы «-1» не может быть преобразовано в «byte».

потому что тип byte может иметь значения от 0 до 255. При этом, в перечислениях константы могут иметь одинаковые значения или использоваться для определения других констант:

enum Days : byte
{
    Monday = 1,
    Tuesday = 2,
    Wednesday = 3,
    Thursday = 5,
    Friday = 5,
    Saturday = 1,
    Sunday = Saturday + 2
}

Пример использования перечислений

Рассмотрим следующий пример использования перечислений в C#

Console.WriteLine("Введите первое целое число:");
int num1 = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Введите второе целое число:");
int num2 = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("0 - сложение");
Console.WriteLine("1 - вычитание");
Console.WriteLine("2 - умножение");
Console.WriteLine("3 - деление");
Operation operNum = (Operation)Convert.ToInt32(Console.ReadLine());
switch (operNum)
{
    case Operation.Add:
        {
            Console.WriteLine($"{num1} + {num2} = {num1 + num2}");
            break;
        }
    case Operation.Subtract:
        {
            Console.WriteLine($"{num1} - {num2} = {num1 - num2}");
            break;
        }
    case Operation.Multiply:
        {
            Console.WriteLine($"{num1} * {num2} = {num1 * num2}");
            break;
        }
    case Operation.Divide:
        {
            Console.WriteLine($"{num1} / {num2} = {(double)num1 / num2}");
            break;
        }
}

enum Operation
{
    Add,
    Subtract,
    Multiply,
    Divide
}

Вначале мы просим ввести пользователя два целых числа:

Console.WriteLine("Введите первое целое число:");
int num1 = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Введите второе целое число:");
int num2 = Convert.ToInt32(Console.ReadLine());

Метод Convert.ToInt32() позволяет нам преобразовать строку в число. Про класс Convert и его методы преобразования есть отдельная заметка. Далее, пользователь выбирает одну из четырех операций — сложение, вычитание, умножение, деление и вводит число от 0 до 3, соответствующее выбранной операции. Тип перечисления описан ниже — Operation. И здесь стоит обратить внимание на следующую строку:

Operation operNum = (Operation)Convert.ToInt32(Console.ReadLine());

здесь мы, во-первых, получаем из строки число — Convert.ToInt32(Console.ReadLine()), а, во-вторых присваиваем переменной operNum значение из перечисления Operation, соответствующее этому числу: Operation operNum = (Operation)[результат]. Чтобы было понятнее, то же самое действие мы могли бы расписать в коде более подробно, используя промежуточную переменную:

int op = Convert.ToInt32(Console.ReadLine());//получили число от 0 до 3
Operation operNum = (Operation)op; //получили значение из Operation

Далее мы, используя оператор выбора switch определяем, что необходимо выполнить с полученными числами и сразу выводим результат пользователю:

switch (operNum) 
{
    case Operation.Add: 
        {
            Console.WriteLine($"{num1} + {num2} = {num1 + num2}");
            break; 
        }
    case Operation.Subtract: 
        {
            Console.WriteLine($"{num1} - {num2} = {num1 - num2}");
            break; 
        }
    case Operation.Multiply: 
        {
            Console.WriteLine($"{num1} * {num2} = {num1 * num2}");
            break; 
        }
    case Operation.Divide: 
        {
            Console.WriteLine($"{num1} / {num2} = {(double)num1 / num2}");
            break; 
        }
}

В итоге, можно получить вот такой результат работы приложения:

Введите первое целое число:
1
Введите второе целое число:
2
0 — сложение
1 — вычитание
2 — умножение
3 — деление
3
1 / 2 = 0,5

Этот пример, конечно, простой и не раскрывает до конца всей важности использования перечислений в C#. По-настоящему, вся красота использования перечислений раскрывается во втором случае — когда константы используются в качестве битовых флагов.

Перечисления C# как битовые флаги

В теме про побитовые операторы и операторы сдвига мы уже затрагивали вопрос об использовании битовых флагов в C#. В языке C# мы можем использовать перечисления enum в качестве битовых флагов. Для демонстрации примера воспользуемся следующим перечислением:

enum Days { 
    Monday, 
    Tuesday, 
    Wednesday, 
    Thursday, 
    Friday,
    Saturday, 
    Sunday
}

Для того, чтобы каждая константа перечисления стала битовым флагом:

  • её значение должно быть степенью двойки
  • к перечислению необходимо применить атрибут [Flags]

Что касается второго пункта — применения атрибутов, то с таким понятием, как атрибуты мы будем довольно часто встречаться, а некоторые из них даже рассматривать подробно. Пока же можно сказать так: атрибут в C# — это специальная конструкция языка (аннотация кода) с помощью которой мы можем указывать компилятору, какой-то части .NET или даже другому программисту, что необходимо сделать с элементом. В нашем случае, мы с помощью атрибута [Flags] указываем компилятору, что будем работать с перечислением как с битовыми флагами. В итоге, наше перечисление Days примет следующий вид:

[Flags]
enum Days { 
    Monday = 1, 
    Tuesday = 2, 
    Wednesday = 4, 
    Thursday = 8, 
    Friday = 16,
    Saturday = 32, 
    Sunday = 64
}

Более того, мы можем создать новый флаг, например, так:

[Flags]
enum Days { 
    Monday = 1, 
    Tuesday = 2, 
    Wednesday = 4, 
    Thursday = 8, 
    Friday = 16,
    Saturday = 32, 
    Sunday = 64,
    Weekend = Saturday | Sunday //применяем логическое сложение
}

последний элемент перечисления получен путем логического сложения дней, относящихся к выходным (субботы и воскресенья). Соответственно, раз мы используем битовые флаги, то для проверки наличия определенного флага в битовой маске мы должны использовать логическое И (логическое умножение). Например,

var monday = Days.Monday;
var saturday = Days.Saturday;
//проверка битового флага среди выходных дней
if ((monday & Days.Weekend) == 0) //проверка отсутствия флага в маске
{
    Console.WriteLine("Понедельник - это рабочий день");
}

if ((saturday & Days.Saturday) != 0) //проверка присутствия флага в маске
{
    Console.WriteLine("Суббота - это выходной день");
}


[Flags]
enum Days
{
    Monday = 1,
    Tuesday = 2,
    Wednesday = 4,
    Thursday = 8,
    Friday = 16,
    Saturday = 32,
    Sunday = 64,
    Weekend = Saturday | Sunday //применяем логическое сложение
}

Второй вариант проверки флага в битовой маске — использование метода enum.HasFlag() для этого перепишем наш код следующим образом:

var monday = Days.Monday;
var saturday = Days.Saturday;
//проверка битового флага среди выходных дней

if (Days.Weekend.HasFlag(monday) == false)
{
    Console.WriteLine("Понедельник - это рабочий день");
}

if (Days.Weekend.HasFlag(saturday) == true)
{
    Console.WriteLine("Суббота - это выходной день");
}

[Flags]
enum Days
{
    Monday = 1,
    Tuesday = 2,
    Wednesday = 4,
    Thursday = 8,
    Friday = 16,
    Saturday = 32,
    Sunday = 64,
    Weekend = Saturday | Sunday //применяем логическое сложение
}

Итого

Использование перечислений enum в C# может носить самый разнообразный характер, например, для хранение состояния какого-либо объекта в программе или, как в нашем случае, для указания типа операции над парой чисел. В любом случае, перечисления помогают нам сделать код программы более понятным. Согласитесь, что, вот такой вызов метода:

DoOperation(10, 3, Ariphm.Divide);

Намного понятнее, чем:

DoOperation(10, 3, -20);

а если учесть, что во втором случае пользователь может вызвать метод и вот так:

DoOperation(10, 3, -134);

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

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии