C# 二进制字面量

232

有没有类似于在 C# 中使用 0x 前缀表示十六进制的二进制字面量写法?0b 不起作用。

如果没有,那么有什么简单的方法可以实现呢?是某种字符串转换吗?


2
使用常量并为它们赋予适当的名称,那么您必须以十进制形式编写它们这一事实就不重要了。个人而言,我希望阅读if (a == MaskForModemIOEnabled) 而不是 if (a == 0b100101) - Lasse V. Karlsen
2
另外注意:如果您找到此帖子是因为想要一个位域,请考虑使用flags属性:http://msdn.microsoft.com/en-us/library/cc138362.aspx#sectionToggle0 - toxvaerd
20
两种都是非常好的建议,除了在定义位域时,这些命名常量定义可以更容易地使用二进制字面值进行阅读和编写。[Flags]枚举数量{ None=0, One=1, Some=0b10, Most=0b100, All=0b111 }。 - brianary
12个回答

231

更新

C# 7.0 现在支持二进制字面量,非常棒。

[Flags]
enum Days
{
    None = 0,
    Sunday    = 0b0000001,
    Monday    = 0b0000010,   // 2
    Tuesday   = 0b0000100,   // 4
    Wednesday = 0b0001000,   // 8
    Thursday  = 0b0010000,   // 16
    Friday    = 0b0100000,   // etc.
    Saturday  = 0b1000000,
    Weekend = Saturday | Sunday,
    Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday
}

原始帖子

由于话题似乎已经转向在枚举中声明基于位的标志值,我认为指出这种情况下一个便利技巧是值得的。左移操作符(<<)可以让你将一个位推到特定的二进制位置。结合在同一类中以其他值为基础声明枚举值的能力,您将拥有一个非常易读的基于位的标志枚举的声明性语法。

[Flags]
enum Days
{
    None        = 0,
    Sunday      = 1,
    Monday      = 1 << 1,   // 2
    Tuesday     = 1 << 2,   // 4
    Wednesday   = 1 << 3,   // 8
    Thursday    = 1 << 4,   // 16
    Friday      = 1 << 5,   // etc.
    Saturday    = 1 << 6,
    Weekend     = Saturday | Sunday,
    Weekdays    = Monday | Tuesday | Wednesday | Thursday | Friday
}

27
很多开发者喜欢这样做。我不如一些开发者擅长心算十六进制数,有时最好迎合最低公共分母。 - StriplingWarrior
2
我认为这是最有效的方法,因为枚举被构建为常量。通过可选的 [Flags] 属性,它们可以用于按位运算(与问题没有直接关系,但在描述二进制字面值时特别有用)。另一个有趣的可能性是将枚举类型强制转换为内置类型(在此示例中,在“Days”之后添加“:byte”);请参阅http://bit.ly/MKv42E 中的内置类型。 - caligari
6
@MikeT:你能详细说明一下(或提供一个详细说明的链接)你最后那个想法吗?我经常声明以“1”开头的枚举,因为我想要能够检测并快速失败,当一个值从未被初始化时。但有些情况下默认值实际上是有意义的,我认为添加一个名称来表示该值不会有任何问题。 - StriplingWarrior
4
根据ca1008的说明,"如果应用了FlagsAttribute属性的枚举定义了一个零值成员,则其名称应为'None',以指示在枚举中未设置任何值。" 因此,如果它被称为“None”,将其添加作为默认值是完全有效的。总体而言,如果默认初始化字段的值实际上有一个名称来显示,这似乎更加清晰。 - Nyerguds
1
@JohnDemetriou。在编译后的代码中,这两者没有任何区别,原因是011一样。但是包含这些零可以帮助开发人员可视化地了解二进制位的位置。 - StriplingWarrior
显示剩余8条评论

182
C#7.0支持二进制字面值(以及通过下划线字符的可选数字分隔符)。
一个例子:
int myValue = 0b0010_0110_0000_0011;

你也可以在Roslyn GitHub页面上找到更多信息。

1
需要记住的是C#的二进制文字是大端序的,但在x86整数中是小端序的,因此Int16 = 0b0010_0110_0000_0011将被存储为{0b0000_0011, 0b0010_0110} - 这很令人困惑。 - Dai
2
@Dai 这与十六进制字面量没有区别。换句话说,所有常量都是大端序的,但在 Intel/AMD 和大多数 ARM 上存储为小端序。0xDEADBEEF将被存储为0xEF 0xBE 0xAD 0xDE - Cole Tobin
这是因为人类语言(至少西方语言)是大端字节序,而源代码中的数字文字也符合这一规则。谢天谢地。 - Oskar Berggren

118

很遗憾,只能直接使用整数和十六进制(ECMA 334v4):

9.4.4.2 整数字面值 整数字面值用于表示 int、uint、long 和 ulong 类型的值。整数字面值有两种可能的形式:十进制和十六进制。

如果要解析,可以使用以下方法:

int i = Convert.ToInt32("01101101", 2);

27

在@StriplingWarrior的有关枚举中的位标志的回答中,还有一种简单的约定可以用于十六进制计数器逐渐通过位移。 使用序列1-2-4-8,向左移动一列并重复。

[Flags]
enum Scenery
{
  Trees   = 0x001, // 000000000001
  Grass   = 0x002, // 000000000010
  Flowers = 0x004, // 000000000100
  Cactus  = 0x008, // 000000001000
  Birds   = 0x010, // 000000010000
  Bushes  = 0x020, // 000000100000
  Shrubs  = 0x040, // 000001000000
  Trails  = 0x080, // 000010000000
  Ferns   = 0x100, // 000100000000
  Rocks   = 0x200, // 001000000000
  Animals = 0x400, // 010000000000
  Moss    = 0x800, // 100000000000
}

从右列开始向下扫描,注意模式1-2-4-8(移位)1-2-4-8(移位)...


回答原问题,我支持 @Sahuagin 的建议,使用十六进制字面量。如果你经常使用二进制数,这很值得你去掌握十六进制。

如果你需要在源代码中看到二进制数字,我建议像我上面那样添加带有二进制字面量的注释。


你可以像这样更容易地实现: Trees = 0x001, Grass = 1 << 1, Flowers = 1 << 2, Cactus = 1 << 3, };``` 你只需要从0或1开始,然后每个接下来的1(从2开始)都会简单地左移一个递增的操作数。<< 1、<< 2、<< 3、<< 4、<< 5等等。当需要更改大型枚举时,更容易正确编辑。 (编辑:如果有人能帮忙更好地格式化,请务必帮忙) - Aaron Carter

25

您始终可以创建准字面量,即包含您需要的值的常量:

const int b001 = 1;
const int b010 = 2;
const int b011 = 3;
// etc ...
Debug.Assert((b001 | b010) == b011);

如果您经常使用它们,可以将它们包装在一个静态类中以供重用。

但是,有点离题,如果您有与位相关联的语义(在编译时已知),我建议使用枚举:

enum Flags
{ 
    First = 0,
    Second = 1,
    Third = 2,
    SecondAndThird = 3
}
// later ...
Debug.Assert((Flags.Second | Flags.Third) == Flags.SecondAndThird);

18
如果您查看.NET编译器平台("Roslyn")的语言功能实现状态,您可以清楚地看到在C# 6.0中这是计划中的功能,因此在下一个版本中我们可以以通常的方式执行它。 二进制字面量状态

3
string sTable="static class BinaryTable\r\n{";
string stemp = "";
for (int i = 0; i < 256; i++)
{
stemp = System.Convert.ToString(i, 2);
while(stemp.Length<8) stemp = "0" + stemp;
sTable += "\tconst char nb" + stemp + "=" + i.ToString() + ";\r\n";
}
sTable += "}";
Clipboard.Clear();
Clipboard.SetText ( sTable);
MessageBox.Show(sTable);

使用这个方法,对于8位二进制,我使用这个方法来创建一个静态类并将其放入剪贴板。然后将其粘贴到项目中并添加到Using部分,这样任何具有nb001010的内容都会从表格中取出,至少是静态的,但仍然... 我在很多PIC图形编码中使用C#,并且在Hi-Tech C中经常使用0b101010。
--代码输出示例--
static class BinaryTable
{   const char nb00000000=0;
    const char nb00000001=1;
    const char nb00000010=2;
    const char nb00000011=3;
    const char nb00000100=4;
//etc, etc, etc, etc, etc, etc, etc, 
}

:-) NEAL


3

在C# 6.0和Visual Studio 2015中,并没有实现二进制字面量特性。但是,在2016年3月30日,Microsoft宣布了新版本的Visual Studio '15' Preview,在这个版本中我们可以使用二进制字面量。

我们可以使用一个或多个下划线(_)字符作为数字分隔符。因此,代码片段可能如下所示:

int x           = 0b10___10_0__________________00; //binary value of 80
int SeventyFive = 0B100_________1011; //binary value of 75

WriteLine($" {x} \n {SeventyFive}");

我们可以使用0b或0B中的任意一个,如上面的代码片段所示。

如果您不想使用数字分隔符,可以像下面的代码片段一样不使用它。

int x           = 0b1010000; //binary value of 80
int SeventyFive = 0B1001011; //binary value of 75

WriteLine($" {x} \n {SeventyFive}");

2

虽然使用Literal不可能,但也许BitConverter可以是一个解决方案?


BitConverter会变得有些棘手,因为它是机器端字节序;对于标准的英特尔处理器来说,这意味着小端字节序。大多数人都很难阅读小端字面值... - Marc Gravell
1
哎呀,现在我记得为什么我总是知道但从未使用过BitConverter了... - Michael Stum
4
BitConverter 类有一个字段 BitConverter.IsLittleEndian,你可以使用它来测试(并反转缓冲区),以判断主机是否为小端字节序。 - foxy

1

虽然字符串解析方案是最流行的,但我不喜欢它,因为在某些情况下解析字符串可能会对性能造成很大的影响。

当需要一种位域或二进制掩码时,我宁愿像这样编写:

long bitMask = 1011001;

然后

int bit5 = BitField.GetBit(bitMask, 5);

或者

bool flag5 = BitField.GetFlag(bitMask, 5);`

其中BitField类是

public static class BitField
{
    public static int GetBit(int bitField, int index)
    {
        return (bitField / (int)Math.Pow(10, index)) % 10;
    }

    public static bool GetFlag(int bitField, int index)
    {
        return GetBit(bitField, index) == 1;
    }
}

4
为什么不使用枚举呢?用一千来代表 1-0-0-0 只会让事情变得麻烦。 - Clément

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接