На всю жизнь я не могу вспомнить, как устанавливать, удалять, переключать или тестировать немного в битовом поле. Либо я не уверен, либо я их перепутал, потому что они мне редко нужны. Так что было бы неплохо иметь "бит-шпаргалку".
Например:
flags = flags | FlagsEnum.Bit4; // Set bit 4.
или
if ((flags & FlagsEnum.Bit4)) == FlagsEnum.Bit4) // Is there a less verbose way?
Можете ли вы привести примеры всех других распространенных операций, предпочтительно в синтаксисе C # с использованием перечисления [Flags]?
Ответы:
Я проделал еще несколько работ над этими расширениями - вы можете найти код здесь
Я написал несколько методов расширения, которые расширяют System.Enum, которые я часто использую ... Я не утверждаю, что они пуленепробиваемые, но они помогли ... Комментарии удалены ...
Затем они используются следующим образом
источник
HasFlag
методEnum
требует бокса.В .NET 4 теперь вы можете написать:
источник
FlagsEnum
это уродливое имя. :)[Flags]
перечисления должны иметь множественные имена, поэтому у имени возникаютFlagsEnum
даже более серьезные проблемы, чем уродство.Идиома состоит в том, чтобы использовать битовый или равный оператор для установки битов:
Чтобы немного прояснить, идиома заключается в использовании побитового и с отрицанием:
Иногда у вас есть смещение, которое идентифицирует ваш бит, и тогда идиома состоит в том, чтобы использовать их в сочетании со сдвигом влево:
источник
@Нарисовался
Обратите внимание, что за исключением простейших случаев, Enum.HasFlag несет в себе значительное снижение производительности по сравнению с написанием кода вручную. Рассмотрим следующий код:
Более 10 миллионов итераций, метод расширения HasFlags занимает колоссальные 4793 мс, по сравнению с 27 мс для стандартной побитовой реализации.
источник
HasFlag
Метод включает в себя бокс / распаковка, на долю которого приходится эту разницу. Но стоимость настолько тривиальна (0,4 мкс), что если вы не находитесь в тесном цикле, я бы взял более читабельный (и менее вероятный глючный) декларативный вызов API в любой день.Встроенные в .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)
Получить флаги
.СЕТЬ
Enums.NET
flags.GetFlags()
Я пытаюсь включить эти улучшения в .NET Core и, возможно, в конечном итоге в полную версию .NET Framework. Вы можете проверить мое предложение здесь .
источник
Синтаксис C ++, предполагая, что бит 0 равен LSB, предполагая, что flags не имеет длинных знаков:
Проверьте, установлено ли:
Проверьте, не установлено ли:
Устанавливать:
Очистить:
Переключение:
источник
Для лучшей производительности и отсутствия мусора используйте это:
источник
Чтобы проверить немного, вы должны сделать следующее: (при условии, что флаги - это 32-битное число)
Тестовый бит:
(Если бит 4 установлен, то он имеет значение true) Переключить назад (1 - 0 или 0 - 1): Сбросить бит 4 в ноль:источник
~0x08
вместо0xFFFFFFF7
... (фактическая маска для 0x8)Это было вдохновлено использованием Sets в качестве индексаторов в Delphi, когда:
источник
C ++ операции: & | ^ ~ (для и, или, xor и не побитовых операций). Также представляют интерес >> и <<, которые являются операциями битового сдвига.
Таким образом, чтобы проверить, установлен ли бит во флаге, вы должны использовать: if (flags & 8) // тестовый бит 4 установлен
источник