关于c#:为什么标志枚举通常用十六进制值定义

Why are flag enums usually defined with hexadecimal values

我经常看到使用十六进制值的标志枚举声明。例如:

1
2
3
4
5
6
7
8
9
10
[Flags]
public enum MyEnum
{
    None  = 0x0,
    Flag1 = 0x1,
    Flag2 = 0x2,
    Flag3 = 0x4,
    Flag4 = 0x8,
    Flag5 = 0x10
}

当我声明枚举时,我通常这样声明它:

1
2
3
4
5
6
7
8
9
10
[Flags]
public enum MyEnum
{
    None  = 0,
    Flag1 = 1,
    Flag2 = 2,
    Flag3 = 4,
    Flag4 = 8,
    Flag5 = 16
}

为什么有些人选择用十六进制而不是十进制来写这个值呢?在我看来,当使用十六进制值并意外地写入Flag5 = 0x16而不是Flag5 = 0x10时,更容易被混淆。


理性可能有所不同,但我看到的一个优点是十六进制提醒你:"好吧,我们不再处理以十为基数的任意人类发明世界中的数字。我们要处理的是位——机器的世界——我们要按照它的规则来处理。"十六进制很少被使用,除非你处理的是相对低级的主题,而数据的内存布局很重要。使用它暗示了这样一个事实,那就是我们现在的处境。

另外,我不确定C,但我知道在C x << y中是一个有效的编译时常量。使用位移似乎最清楚:

1
2
3
4
5
6
7
8
9
10
[Flags]
public enum MyEnum
{
    None  = 0,
    Flag1 = 1 << 0,
    Flag2 = 1 << 1,
    Flag3 = 1 << 2,
    Flag4 = 1 << 3,
    Flag5 = 1 << 4
}


这使得很容易看到这些是二进制标志。

1
2
3
4
5
6
None  = 0x0,  // == 00000
Flag1 = 0x1,  // == 00001
Flag2 = 0x2,  // == 00010
Flag3 = 0x4,  // == 00100
Flag4 = 0x8,  // == 01000
Flag5 = 0x10  // == 10000

尽管这一进展使情况更加明朗:

1
2
3
Flag6 = 0x20  // == 00100000
Flag7 = 0x40  // == 01000000
Flag8 = 0x80  // == 10000000


我认为这只是因为序列总是1,2,4,8,然后加上0。
如您所见:

1
2
3
4
5
6
7
8
9
10
11
12
0x1 = 1
0x2 = 2
0x4 = 4
0x8 = 8
0x10 = 16
0x20 = 32
0x40 = 64
0x80 = 128
0x100 = 256
0x200 = 512
0x400 = 1024
0x800 = 2048

等等,只要你记住序列1-2-4-8,你就可以构建所有随后的标记,而不必记住2的幂次


因为[Flags]意味着枚举实际上是一个位字段。使用[Flags]可以使用按位和(&和或(|运算符)组合标志。当处理这样的二进制值时,几乎总是更清楚地使用十六进制值。这就是我们最初使用十六进制的原因。每个十六进制字符正好对应一个半字节(四位)。对于decimal,这个1到4的映射不成立。


因为有一种机械的,简单的方法可以使两个六角的幂加倍。在十进制中,这很难。这需要你头脑中长时间的乘法运算。十六进制是一个简单的变化。你可以一直执行到1UL << 63,这是你不能用十进制来实现的。


因为在旗子上的位更容易跟随人类。每个十六进制数字可以容纳4位二进制。

1
2
3
4
5
6
7
8
0x0 = 0000
0x1 = 0001
0x2 = 0010
0x3 = 0011

... and so on

0xF = 1111

通常,您希望标记不重叠位,最简单的方法是使用十六进制值来声明标记。

因此,如果需要16位的标志,您将使用4位十六进制值,这样可以避免错误的值:

1
2
3
4
5
6
7
8
9
0x0001 //= 1 = 000000000000 0001
0x0002 //= 2 = 000000000000 0010
0x0004 //= 4 = 000000000000 0100
0x0008 //= 8 = 000000000000 1000
...
0x0010 //= 16 = 0000 0000 0001 0000
0x0020 //= 32 = 0000 0000 0010 0000
...
0x8000 //= 32768 = 1000 0000 0000 0000