【问题标题】:Why are flag enums usually defined with hexadecimal values为什么标志枚举通常用十六进制值定义
【发布时间】:2012-10-24 17:33:50
【问题描述】:

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

[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

【问题讨论】:

  • 如果您使用十进制数字,那么您写10 而不是0x10 的可能性会降低吗?特别是因为这些是我们正在处理的二进制数,而十六进制可以轻松地转换为二进制数? 0x111 在脑海中翻译比273 更烦人...
  • 遗憾的是,C# 没有明确要求写出二的幂的语法。
  • 你在这里做了一些荒谬的事情。标志背后的意图是它们将按位组合。但按位组合不是该类型的元素。 Flag1 | Flag2的值为3,3不对应MyEnum的任何域值。
  • 你在哪里看到的?带反光板?
  • @giammin 这是一个普遍的问题,而不是具体的实现。例如,您可以采用开源项目或仅提供网络上可用的代码。

标签: c# .net enums enum-flags


【解决方案1】:

原理可能不同,但我看到的一个优势是十六进制提醒您:“好吧,我们不再处理人类发明的以十为底的任意世界中的数字了。我们处理的是比特 - 机器的世界- 我们会按照它的规则行事。”除非您正在处理数据的内存布局很重要的相对较低级别的主题,否则很少使用十六进制。使用它暗示了我们现在所处的情况。

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

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

【讨论】:

  • 这很有趣,它实际上也是一个有效的 C# 枚举。
  • +1 使用这个符号你永远不会在枚举值计算中出错
  • @AllonGuralnek:编译器是否在给定 [Flags] 注释的情况下分配唯一的位位置?通常它从 0 开始并以 1 为增量,因此任何分配为 3(十进制)的枚举值将是二进制的 11,设置两位。
  • @Eric:嗯,我不知道为什么,但我总是确定它确实分配了 2 的幂的值。我刚查了一下,我想我错了。
  • x &lt;&lt; y 符号的另一个有趣事实。 1 &lt;&lt; 10 = KB1 &lt;&lt; 20 = MB1 &lt;&lt; 30 = GB 等等。如果你想为缓冲区创建一个 16 KB 的数组,那真的很好,你可以去var buffer = new byte[16 &lt;&lt; 10];
【解决方案2】:

很容易看出这些是二进制标志。

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

【讨论】:

  • 我实际上在 0x1、0x2、0x4、0x8 前面添加了一个 0...所以我得到 0x01、0x02、0x04、0x08 和 0x10...我发现这样更容易阅读。我是不是搞砸了?
  • @Light - 一点也不。这很常见,因此您可以看到它们是如何对齐的。只是让这些位更明确:)
  • @LightStriker 只是要把它扔出去,如果你不使用十六进制,确实很重要。仅以零开头的值被解释为八进制。所以012实际上是10
  • @JonathonRainhart:我知道。但是我在使用位域时总是使用十六进制。我不确定使用int 是否会感到安全。我知道这很愚蠢……但习惯很难改掉。
  • 我认为的最佳答案。 +1 :)
【解决方案3】:

我认为只是因为序列总是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 的幂

【讨论】:

    【解决方案4】:

    因为[Flags] 表示枚举实际上是bitfield。使用[Flags],您可以使用按位AND (&amp;) 和OR (|) 运算符来组合标志。在处理这样的二进制值时,使用十六进制值几乎总是更清楚。这就是我们首先使用hexadecimal 的原因。每个十六进制字符恰好对应一个半字节(四位)。对于十进制,这种 1 到 4 映射不成立。

    【讨论】:

    • 使用按位运算的能力实际上与 flags 属性无关。
    【解决方案5】:

    因为有一种简单的机械方法可以将十六进制的二的幂加倍。在十进制中,这很难。它需要在您的脑海中进行长时间的乘法运算。在十六进制中,这是一个简单的更改。您可以一直执行到1UL &lt;&lt; 63,这是您无法以十进制执行的。

    【讨论】:

    • 我认为这是最有意义的。对于具有大量值的枚举,这是最简单的方法(@Hypercube 的示例可能除外)。
    【解决方案6】:

    因为标记中的位更容易被人类遵循。每个十六进制数字可以容纳一个 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
    

    【讨论】:

    • 这个解释很好......唯一缺少的是二进制等价物来显示位、半字节和字节的最终排列;)
    猜你喜欢
    • 2011-08-05
    • 1970-01-01
    • 2012-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多