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