在我的生活中,我不记得如何设置,删除,切换或测试位域中的位。要么我不确定,要么我把它们弄混了,因为我很少需要它们。所以有一个“bit-cheat-sheet”会很好。

例如:

flags = flags | FlagsEnum.Bit4;  // Set bit 4.

or

if ((flags & FlagsEnum.Bit4)) == FlagsEnum.Bit4) // Is there a less verbose way?

你能给出所有其他常见操作的例子吗,最好是在c#语法中使用[Flags]枚举?


c++操作为:& | ^ ~(用于和,或,xor,而不是位操作)。还有>>和<<,它们是位移位操作。

因此,要测试是否在标志中设置了位,你可以使用: 如果(flags & 8) //测试位4已设置


习惯用法是使用按位或等于操作符来设置位:

flags |= 0x04;

为了澄清一点,这个习语是按位使用,并带否定:

flags &= ~0x04;

有时你有一个偏移量来标识你的位,然后习惯用法是将这些偏移量与左移结合使用:

flags |= 1 << offset;
flags &= ~(1 << offset);

c++语法,假设第0位是LSB,假设flags是unsigned long:

检查是否设置:

flags & (1UL << (bit to test# - 1))

检查是否未设置:

invert test !(flag & (...))

Set:

flag |= (1UL << (bit to set# - 1))

明确:

flag &= ~(1UL << (bit to clear# - 1))

切换:

flag ^= (1UL << (bit to set# - 1))

为了测试一点,你可以做以下事情: (假设flags是一个32位的数字)

测试点: if((flags & 0x08) == 0x08)(如果第4位设置为真) 切换回(1 - 0或0 - 1):flags = flags ^ 0x08; 重置位4到零:flags = flags & 0xFFFFFF7F;


我在这些扩展上做了更多的工作-你可以在这里找到代码

我写了一些扩展方法来扩展System。枚举,我经常使用…我并不是说它们是无懈可击的,但它们确实有所帮助……删除注释……

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

在。net 4中,你现在可以这样写:

flags.HasFlag(FlagsEnum.Bit4)

@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();
    }
}

在1000万次迭代中,HasFlags扩展方法花费了4793毫秒,而标准的逐位实现只需要27毫秒。


这是受到在Delphi中使用set作为索引器的启发,在很久以前:

/// 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];

  }
}

. net的内置标志枚举操作非常有限。大多数情况下,用户需要弄清楚按位操作的逻辑。

在。net 4中,HasFlag方法被添加到Enum中,这有助于简化用户的代码,但不幸的是,它存在许多问题。

HasFlag不是类型安全的,因为它接受任何类型的枚举值参数,而不仅仅是给定的枚举类型。 HasFlag对于是否检查值是否具有enum value参数提供的所有或任何标志具有歧义性。顺便说一下。 HasFlag相当慢,因为它需要装箱,这会导致分配和更多的垃圾收集。

由于. net对标志枚举的支持有限,我编写了OSS库enums。NET解决了这些问题,使处理标志枚举更加容易。

下面是它提供的一些操作以及仅使用. net框架的等效实现。

把国旗

.NET标志| otherFlags

Enums.NET 标志。CombineFlags(otherFlags)


删除标志

.NET标志& ~otherFlags

Enums.NET 标志。删除标志(其他标志)


常见的旗帜

.NET标志和其他标志

Enums.NET 标志。CommonFlags(otherFlags)


切换的旗帜

.NET flags ^ otherFlags

Enums.NET 标志。切换标志(其他标志)


拥有所有旗帜

.NET (flags & otherFlags) == otherFlags或flags. hasflag (otherFlags)

Enums.NET 标志。HasAllFlags(otherFlags)


是否有旗帜

.NET (flags & otherFlags) != 0

Enums.NET 标志。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 标志。GetFlags()


我正试图将这些改进整合到。net Core中,并最终整合到完整的。net框架中。你可以在这里查看我的提案。


为了获得最佳性能和零垃圾,请使用以下代码:

using System;
using T = MyNamespace.MyFlags;

namespace MyNamespace
{
    [Flags]
    public enum MyFlags
    {
        None = 0,
        Flag1 = 1,
        Flag2 = 2
    }

    static class MyFlagsEx
    {
        public static bool Has(this T type, T value)
        {
            return (type & value) == value;
        }

        public static bool Is(this T type, T value)
        {
            return type == value;
        }

        public static T Add(this T type, T value)
        {
            return type | value;
        }

        public static T Remove(this T type, T value)
        {
            return type & ~value;
        }
    }
}

按位(标志)enum指南

老了,但想试着做个小抄,即使是供我自己参考:

Operation Syntax Example
On |= e |= E.A
Off &= + ~ e &= ~E.A
Toggle ^= e ^= E.A
Test (.NET API) .HasFlag e.HasFlag(E.A)
Test (bitwise) (see example) (e & E.A) == E.A

例子

[Flags]
enum E {
    A = 0b1,
    B = 0b10,
    C = 0b100
}

E e = E.A;        // Assign (e = A)
e |= E.B | E.C;   // Add    (e = A, B, C)
e &= ~E.A & ~E.B; // Remove (e = C) -- alt syntax: &= ~(E.A | E.B)
e ^= E.A | E.C;   // Toggle (e = A)
e.HasFlag(E.A);   // Test   (returns true)

// Testing multiple flags using bit operations:
bool hasAandB = ( e & (E.A | E.B) ) == (E.A | E.B);

好处:定义一个Flags enum

通常,我们像这样使用整数:

[Flags]
enum E {
    A = 1,
    B = 2,
    C = 4,
    // etc.

但当我们接近更大的数字时,计算下一个值就不那么容易了:

  // ...
  W = 4194304,
  X = 8388608,
  // ..

然而,有两个替代方案:二进制和十六进制字面量。

对于二进制,只需在前一个值的末尾追加一个0:

[Flags]
enum E {
    A = 0b1,
    B = 0b10,
    C = 0b100,
    // ...
    W = 0b100_0000_0000_0000_0000_0000,
    X = 0b1000_0000_0000_0000_0000_0000,

十六进制也有一个方便的模式,可能看起来不那么丑陋:循环1、2、4、8,在每次完整迭代后加一个0。

[Flags]
enum E {
    A = 0x1,
    B = 0x2,
    C = 0x4,
    D = 0x8,
    E = 0x10, // 16
    F = 0x20, // 32, etc.
    // ...
    W = 0x400000,
    X = 0x800000,