Перечисления можно использовать для хранения битовых флагов, благодаря этому экземпляр enum может содержать в себе комбинацию значений констант, определенных в списке. Для создания перечисления с битовыми флагами, используется атрибут [Flags]. Значения констант задаются таким образом, чтобы к ним можно было применять битовые операции.
Для использования перечислений как битовых флагов необходимо соблюдать требования:
- Обязательно включайте в перечисление константу со значением 0, обычно используется имя None;
- Константы должны быть степенями двойки 1, 2, 4… Это гарантирует что комбинации флагов не будут перекрываться;
- Если сочетания флагов часто используются, добавьте их в перечисление;
- Для констант используйте только положительные значения, поскольку отрицательные могут внести неоднозначность.
Определение битовых флагов перечислений
Поскольку константы должны содержать степени двойки, можно задавать их несколькими вариантами:
- Двоичными литералами;
- Числовым литералом с операцией сдвига;
- Числами десятичной системы;
- Литералами шестнадцатеричной системы.
Для присвоения значений константам, проще и нагляднее использовать двоичные литералы:
[Flags]
enum AccessType
{
Deny = 0b00,
ReadOnly = 0b01,
Write = 0b10,
ReadWrite = 0b11 //часто используемая комбинация флагов
}
или операцию сдвига:
[Flags]
enum Border
{
None = 0,
Left = 1 << 0, //1
Top = 1 << 1, //2
Right = 1 << 2, //4
Bottom = 1 << 3 //8
}
Битовые операции
Для комбинирования, сброса и проверки флагов используются битовые операции.
Рассмотрим пример перечисления которое описывает дни недели, значения представим в виде двоичных литералов(можно использовать любую из доступных систем счисления). С помощью битовых операций мы можем ввести переменные которые будут обозначать выходные и рабочие дни недели.
using System;
class Program
{
[Flags]
enum Days
{
Not = 0b00000000,
Mon = 0b00000001,
Tue = 0b00000010,
Wed = 0b00000100,
Thu = 0b00001000,
Fri = 0b00010000,
Sat = 0b00100000,
Sun = 0b01000000,
//флаги можно комбинировать создавая наборы с помощью констант
WorkingDays = Mon | Tue | Wed | Thu | Fri, //0b00011111
//или используя значения
Weekend = 0b01100000 //Sat|Sun
}
static void Main(string[] args)
{
var monday = Days.Mon;
//проверка битового флага
if ((monday & Days.WorkingDays) != 0)
{
Console.WriteLine("Понедельник - рабочий день");
}
var goodDays = Days.Thu | Days.Tue;
var badDays = Days.Fri | Days.Mon;
Console.WriteLine($"Хорошие дни: {goodDays}");
Console.WriteLine($"Плохие дни: {badDays}");
Console.ReadLine();
}
}
Как видно из примера для установки флага используется оператор |(побитовое или):
var x = Days.Mon | Days.Tue;
x |= Days.Fri;
Console.WriteLine(x);
Для проверки флага используется конструкция с оператором &(побитовое и):
bool flag1 = (x & Days.Fri) == Days.Fri; //true
//или
bool flag2 = (x & Days.Fri) != 0; //true
Для сброса флага можно использовать оператор ^(xor):
x = x ^ Days.Tue;
Console.WriteLine(x);
Битовые маски широко используются в среде .Net, в частности для указания привязки объектов к контейнерам(Anchor) и определения уровня доступа к файлам(FileAccess), это лишь малая часть из перечислений которые входят в состав .Net Framework.