Наиболее распространенные побитовые операции С# на перечислениях
В течение жизни я не могу вспомнить, как устанавливать, удалять, переключать или тестировать бит в битовом поле. Либо я не уверен, либо смешиваю их, потому что мне они редко нужны. Поэтому было бы неплохо иметь "бит-чит-лист".
Например:
flags = flags | FlagsEnum.Bit4; // Set bit 4.
или
if ((flags & FlagsEnum.Bit4)) == FlagsEnum.Bit4) // Is there a less verbose way?
Можете ли вы привести примеры всех других общих операций, предпочтительно в синтаксисе С# с использованием перечисления [Flags]?
Ответы
Ответ 1
Я сделал еще немного работы над этими расширениями - Здесь вы можете найти код
Я написал несколько методов расширения, которые расширяют System.Enum, которые я часто использую... Я не утверждаю, что они пуленепробиваемые, но они помогли... Комментарии удалены...
namespace Enum.Extensions {
public static class EnumerationExtensions {
public static bool Has<T>(this System.Enum type, T value) {
try {
return (((int)(object)type & (int)(object)value) == (int)(object)value);
}
catch {
return false;
}
}
public static bool Is<T>(this System.Enum type, T value) {
try {
return (int)(object)type == (int)(object)value;
}
catch {
return false;
}
}
public static T Add<T>(this System.Enum type, T value) {
try {
return (T)(object)(((int)(object)type | (int)(object)value));
}
catch(Exception ex) {
throw new ArgumentException(
string.Format(
"Could not append value from enumerated type '{0}'.",
typeof(T).Name
), ex);
}
}
public static T Remove<T>(this System.Enum type, T value) {
try {
return (T)(object)(((int)(object)type & ~(int)(object)value));
}
catch (Exception ex) {
throw new ArgumentException(
string.Format(
"Could not remove value from enumerated type '{0}'.",
typeof(T).Name
), ex);
}
}
}
}
Затем они используются, как показано ниже:
SomeType value = SomeType.Grapes;
bool isGrapes = value.Is(SomeType.Grapes); //true
bool hasGrapes = value.Has(SomeType.Grapes); //true
value = value.Add(SomeType.Oranges);
value = value.Add(SomeType.Apples);
value = value.Remove(SomeType.Grapes);
bool hasOranges = value.Has(SomeType.Oranges); //true
bool isApples = value.Is(SomeType.Apples); //false
bool hasGrapes = value.Has(SomeType.Grapes); //false
Ответ 2
В .NET 4 вы можете написать:
flags.HasFlag(FlagsEnum.Bit4)
Ответ 3
Идиома заключается в использовании побитового или равного оператора для установки бит:
flags |= 0x04;
Чтобы очистить бит, идиома должна использоваться поразрядно и с отрицанием:
flags &= ~0x04;
Иногда у вас есть смещение, которое идентифицирует ваш бит, а затем идиома должна использовать их в сочетании с левым сдвигом:
flags |= 1 << offset;
flags &= ~(1 << offset);
Ответ 4
@Drew
Обратите внимание, что, за исключением простейшего случая, Enum.HasFlag несет в себе значительное снижение производительности по сравнению с написанием кода вручную. Рассмотрим следующий код:
[Flags]
public enum TestFlags
{
One = 1,
Two = 2,
Three = 4,
Four = 8,
Five = 16,
Six = 32,
Seven = 64,
Eight = 128,
Nine = 256,
Ten = 512
}
class Program
{
static void Main(string[] args)
{
TestFlags f = TestFlags.Five; /* or any other enum */
bool result = false;
Stopwatch s = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
result |= f.HasFlag(TestFlags.Three);
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds); // *4793 ms*
s.Restart();
for (int i = 0; i < 10000000; i++)
{
result |= (f & TestFlags.Three) != 0;
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds); // *27 ms*
Console.ReadLine();
}
}
Более 10 миллионов итераций, метод расширения HasFlags занимает колоссальные 4793 мс, по сравнению с 27 мс для стандартной побитовой реализации.
Ответ 5
Синтаксис С++, предполагая, что бит 0 является LSB, предполагая, что флаги unsigned long:
Убедитесь, что Set:
flags & (1UL << (bit to test# - 1))
Проверьте, не установлен ли:
invert test !(flag & (...))
Set:
flag |= (1UL << (bit to set# - 1))
Clear:
flag &= ~(1UL << (bit to clear# - 1))
Переключение:
flag ^= (1UL << (bit to set# - 1))
Ответ 6
Встроенные операции перечисления в .NET, к сожалению, весьма ограничены. Большую часть времени пользователи остаются с вычислением побитовой логики работы.
В .NET 4 к Enum
добавлен метод HasFlag
, который упрощает код пользователя, но, к сожалению, с ним много проблем.
-
HasFlag
не является безопасным по типу, поскольку он принимает любой тип аргумента значения enum, а не только указанный тип перечисления.
-
HasFlag
является неоднозначным в отношении того, проверяет ли он, имеет ли значение все или любые флаги, предоставленные аргументом значения enum. Все это, кстати.
-
HasFlag
довольно медленный, так как он требует бокса, который вызывает выделения и, следовательно, больше коллекций мусора.
Отчасти благодаря ограниченной поддержке .NET для перечислений флагов я написал библиотеку OSS Enums.NET, которая адресует каждую из этих проблем и упрощает работу с перечислениями флагов.
Ниже приведены некоторые из операций, которые он предоставляет вместе с их эквивалентными реализациями, используя только .NET framework.
Объединить флаги
.NET flags | otherFlags
Enums.NET flags.CombineFlags(otherFlags)
Удалить флаги
.NET flags & ~otherFlags
Enums.NET flags.RemoveFlags(otherFlags)
Общие флаги
.NET flags & otherFlags
Enums.NET flags.CommonFlags(otherFlags)
Включить флаги
.NET flags ^ otherFlags
Enums.NET flags.ToggleFlags(otherFlags)
Имеет все флаги
.NET (flags & otherFlags) == otherFlags
или flags.HasFlag(otherFlags)
Enums.NET flags.HasAllFlags(otherFlags)
Имеет ли какие-либо флаги
.NET (flags & otherFlags) != 0
Enums.NET flags.HasAnyFlags(otherFlags)
Получить флаги
.NET
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. Вы можете проверить мое предложение здесь.
Ответ 7
Чтобы проверить бит, вы должны сделать следующее:
(предполагая, что флаги - 32-битное число)
Тест бит:
if((flags & 0x08) == 0x08)
(Если бит 4 установлен, то его значение равно true)
Переключить назад (1 - 0 или 0 - 1): flags = flags ^ 0x08;
Reset Бит 4 до нуля: flags = flags & 0xFFFFFF7F;
Ответ 8
Это было вдохновлено использованием Sets в качестве индексаторов в Delphi, назад, когда:
/// Example of using a Boolean indexed property
/// to manipulate a [Flags] enum:
public class BindingFlagsIndexer
{
BindingFlags flags = BindingFlags.Default;
public BindingFlagsIndexer()
{
}
public BindingFlagsIndexer( BindingFlags value )
{
this.flags = value;
}
public bool this[BindingFlags index]
{
get
{
return (this.flags & index) == index;
}
set( bool value )
{
if( value )
this.flags |= index;
else
this.flags &= ~index;
}
}
public BindingFlags Value
{
get
{
return flags;
}
set( BindingFlags value )
{
this.flags = value;
}
}
public static implicit operator BindingFlags( BindingFlagsIndexer src )
{
return src != null ? src.Value : BindingFlags.Default;
}
public static implicit operator BindingFlagsIndexer( BindingFlags src )
{
return new BindingFlagsIndexer( src );
}
}
public static class Class1
{
public static void Example()
{
BindingFlagsIndexer myFlags = new BindingFlagsIndexer();
// Sets the flag(s) passed as the indexer:
myFlags[BindingFlags.ExactBinding] = true;
// Indexer can specify multiple flags at once:
myFlags[BindingFlags.Instance | BindingFlags.Static] = true;
// Get boolean indicating if specified flag(s) are set:
bool flatten = myFlags[BindingFlags.FlattenHierarchy];
// use | to test if multiple flags are set:
bool isProtected = ! myFlags[BindingFlags.Public | BindingFlags.NonPublic];
}
}
Ответ 9
Операции на С++: и | ^ ~ (для и, или, xor и не побитовых операций). Также интересны: < < < < <, которые вл ютс операци ми сдвига.
Итак, чтобы проверить, что бит установлен во флагах, вы должны использовать: if (флаги и 8)//проверен бит 4, установленный