Встроенные в .NET операции перечисления флага, к сожалению, весьма ограничены. Большую часть времени у пользователей остается на выяснение логики побитовой операции.
В .NET 4 HasFlag
был добавлен метод, Enum
который помогает упростить пользовательский код, но, к сожалению, с ним много проблем.
HasFlag
не является типобезопасным, поскольку принимает любой тип аргумента значения перечисления, а не только данный тип перечисления.
HasFlag
неоднозначно, проверяет ли оно, имеет ли значение все или любой из флагов, предоставленных аргументом значения перечисления. Это все кстати.
HasFlag
довольно медленный, так как требует бокса, который вызывает выделение ресурсов и, следовательно, сбор мусора.
Частично из-за ограниченной поддержки перечислений флагов в .NET я написал библиотеку OSS Enums.NET, которая решает каждую из этих проблем и значительно облегчает работу с перечислениями флагов.
Ниже приведены некоторые операции, которые он предоставляет вместе с эквивалентными реализациями, использующими только .NET Framework.
Объединить флаги
.СЕТЬ flags | otherFlags
Enums.NET flags.CombineFlags(otherFlags)
Удалить флаги
.СЕТЬ flags & ~otherFlags
Enums.NET flags.RemoveFlags(otherFlags)
Общие флаги
.СЕТЬ flags & otherFlags
Enums.NET flags.CommonFlags(otherFlags)
Флаги переключения
.СЕТЬ flags ^ otherFlags
Enums.NET flags.ToggleFlags(otherFlags)
Имеет все флаги
.NET (flags & otherFlags) == otherFlags
илиflags.HasFlag(otherFlags)
Enums.NET flags.HasAllFlags(otherFlags)
Есть флаги
.СЕТЬ (flags & otherFlags) != 0
Enums.NET flags.HasAnyFlags(otherFlags)
Получить флаги
.СЕТЬ
Enumerable.Range(0, 64)
.Where(bit => ((flags.GetTypeCode() == TypeCode.UInt64 ? (long)(ulong)flags : Convert.ToInt64(flags)) & (1L << bit)) != 0)
.Select(bit => Enum.ToObject(flags.GetType(), 1L << bit))`
Enums.NET flags.GetFlags()
Я пытаюсь включить эти улучшения в .NET Core и, возможно, в конечном итоге в полную версию .NET Framework. Вы можете проверить мое предложение здесь .