为什么通常用十六进制值定义标志枚举


121

很多时候,我看到使用十六进制值的标志枚举声明。例如:

[Flags]
public enum MyEnum
{
    None  = 0x0,
    Flag1 = 0x1,
    Flag2 = 0x2,
    Flag3 = 0x4,
    Flag4 = 0x8,
    Flag5 = 0x10
}

当我声明一个枚举时,通常会这样声明:

[Flags]
public enum MyEnum
{
    None  = 0,
    Flag1 = 1,
    Flag2 = 2,
    Flag3 = 4,
    Flag4 = 8,
    Flag5 = 16
}

为什么有些人选择用十六进制而不是十进制写值,是否有原因或理由?从我的角度来看,使用十六进制值时很容易感到困惑,而意外地写成Flag5 = 0x16而不是Flag5 = 0x10


4
是什么让您写10而不是0x10使用十进制数的可能性更低?特别是由于这些是我们正在处理的二进制数,而十六进制可轻松地转换为二进制或从二进制转换为二进制? 0x111273…… 更令人讨厌的翻译
cHao 2012年

4
令人遗憾的是C#没有明确要求写出2的幂的语法。
Panic Panic

您在这里做些荒谬的事情。标志的目的是将它们按位组合。但是按位组合不是该类型的元素。值为Flag1 | Flag23,并且3不对应于的任何域值MyEnum
卡兹(Kaz)2012年

你在哪里看到的?带反射器?
giammin 2012年

@giammin这是一个普遍的问题,而不是特定的实现。例如,您可以使用开源项目,也可以仅使用网上提供的代码。
阿迪·莱斯特

Answers:


183

原理可能有所不同,但我看到的一个优点是十六进制提醒您:“好吧,我们不再在人类创造的以10为基数的任意世界中处理数字。我们正在处理位-机器的世界-而我们将按照其规则行事。” 除非您正在处理数据存储布局很重要的相对较低级别的主题,否则很少使用十六进制。使用它暗示着事实,那就是我们现在所处的情况。

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

[Flags]
public enum MyEnum
{
    None  = 0,
    Flag1 = 1 << 0,
    Flag2 = 1 << 1,
    Flag3 = 1 << 2,
    Flag4 = 1 << 3,
    Flag5 = 1 << 4
}

7
这非常有趣,实际上它也是一个有效的C#枚举。
阿迪·莱斯特

13
使用这种表示法+1,您永远不会在枚举值计算中犯错
Sergey Berezovskiy 2012年

1
@AllonGuralnek:给[Flags]批注时,编译器是否分配唯一的位位置?通常,它从0开始,以1为增量,因此分配为3(十进制)的任何枚举值都将是二进制的11(设置两位)。
Eric J.

2
@Eric:h,我不知道为什么,但是我始终可以确定它确实分配了2的幂。我刚检查,我想我错了。
Allon Guralnek

38
另一个有趣的事实与x << y符号。1 << 10 = KB1 << 20 = MB1 << 30 = GB等等。如果您想为缓冲区创建一个16 KB的数组就可以了,那就很好了var buffer = new byte[16 << 10];
Scott Chamberlain

43

可以很容易地看到它们是二进制标志。

None  = 0x0,  // == 00000
Flag1 = 0x1,  // == 00001
Flag2 = 0x2,  // == 00010
Flag3 = 0x4,  // == 00100
Flag4 = 0x8,  // == 01000
Flag5 = 0x10  // == 10000

尽管进展使它更加清晰:

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

3
实际上,我在0x1、0x2、0x4、0x8前面添加了0,因此我得到0x01、0x02、0x04、0x08和0x10 ...我觉得这更容易阅读。我搞砸了吗?
LightStriker

4
@Light-一点也不。这很常见,因此您可以看到它们如何对齐。只是让内容更明确:)
Oded 2012年

2
@LightStriker只是去把它扔在那里,确实,如果你不使用十六进制的事情。仅以零开头的值被解释为八进制。012其实也是如此10
乔纳森·莱因哈特

@JonathonRainhart:我知道。但是在使用位域时,我总是使用十六进制。我不确定使用它会安全int吗。我知道这很愚蠢……但是习惯会死得很厉害。
LightStriker

36

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

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的幂


13

因为[Flags]意味着该枚举实际上是一个位域。有了[Flags]你可以使用按位AND( &)和OR( |)运营商的标志结合使用。在处理这样的二进制值时,几乎总是更清楚地使用十六进制值。这就是我们首先使用十六进制的原因。每个十六进制字符对应于恰好一个半字节(4位)。使用十进制时,此1到4映射不成立。


2
实际上,使用按位运算的能力与flags属性无关。
Mattias Nordqvist

4

因为有一种机械的简单方法可以将十六进制的二乘幂加倍。用十进制表示,这很难。它需要长时间的繁衍。以十六进制表示,这是一个简单的更改。您可以一直执行此操作,直到1UL << 63十进制都无法执行。


1
我认为这是最合理的。对于具有大量值的枚举,这是最简单的方法(@Hypercube示例除外)。
阿迪·莱斯特

3

因为对于人类来说,在标志中位更容易跟踪。每个十六进制数字都可以适合4位二进制数。

0x0 = 0000
0x1 = 0001
0x2 = 0010
0x3 = 0011

... and so on

0xF = 1111

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

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

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

这种解释是好的。...唯一缺少的是二进制的等效形式,用以显示位,半字节和字节的最终排列;)
GoldBishop
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.