Содержание
Про перечисления (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(...)
— это выражение, с помощью которого можно получить имя переменной в виде строки.и увидеть в консоли следующее:
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# выдаст нам ошибку:
потому что тип 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);
то использование перечислений позволяют сделать код не только более понятным, но и устойчивым к возможным ошибкам со стороны другого разработчика, который будет использовать наш код.