在我的生活中,我不记得如何设置,删除,切换或测试位域中的位。要么我不确定,要么我把它们弄混了,因为我很少需要它们。所以有一个“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]枚举?


当前回答

@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毫秒。

其他回答

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

flags |= 0x04;

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

flags &= ~0x04;

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

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

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

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,

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

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

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

flags.HasFlag(FlagsEnum.Bit4)