Двоичные литералы C #

187

Есть ли способ написать двоичные литералы в C #, например, префикс шестнадцатеричный с 0x? 0b не работает.

Если нет, то какой простой способ сделать это? Какое-то преобразование строк?

toxvaerd
источник
2
Используйте константы и дайте им собственные имена, тогда тот факт, что вы должны записать их в десятичном виде, не должен иметь значения. Лично я хотел бы читать, if (a == MaskForModemIOEnabled)а неif (a == 0b100101)
Лассе В. Карлсен
2
Еще одно примечание: если вы нашли этот пост, потому что вам нужно битовое поле, рассмотрите атрибут flags: msdn.microsoft.com/en-us/library/cc138362.aspx#sectionToggle0
toxvaerd
17
Оба являются очень хорошим советом, за исключением случаев определения указанных битовых полей, где эти определения именованных констант легче читать и записывать с помощью двоичного литерала. [Flags] enum Количество {None = 0, One = 1, Some = 0b10, Most = 0b100, All = 0b111}
brianary
1
Для чего это стоит, поддержка двоичных литералов запланирована для Roslyn .
Джо Уайт
Это все еще не поддерживается в C # 6.0, верно? Не могу использовать двоичные литералы в vs2015: /
anhoppe

Ответы:

146

C # 7.0 поддерживает двоичные литералы (и необязательные разделители цифр через символы подчеркивания).

Пример:

int myValue = 0b0010_0110_0000_0011;

Вы также можете найти больше информации на странице Roslyn GitHub .

BTownTKD
источник
3
@ D.Singh Я вижу это в списке C # 7 сейчас. github.com/dotnet/roslyn/issues/2136
Данация
9
Сладкий! Отлично работает в VS2017 / C # 7.
STLDev
1
Это помогает помнить, что двоичные литералы C # имеют порядок байтов, но в x86 целые числа имеют порядок байтов, поэтому Int16 = 0b0010_0110_0000_0011они будут храниться как { 0b0000_0011, 0b0010_0110 }запутанные.
Дай
1
@Dai ничем не отличается от шестнадцатеричных литералов. Другими словами, все константы с прямым порядком байтов, но хранятся с прямым порядком байтов в Intel / AMD и большинстве ARM. 0xDEADBEEFбудет храниться как0xEF 0xBE 0xAD 0xDE
Коул Джонсон
170

Обновить

В 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
}
StriplingWarrior
источник
26
@ColeJohnson: Многие разработчики предпочитают это. Я не так хорош в преобразовании гексагона в моей голове, как некоторые разработчики, и иногда лучше всего удовлетворить наименьший общий знаменатель.
StriplingWarrior
2
Я думаю, что это наиболее эффективный способ, поскольку перечисления строятся как константы. С необязательным атрибутом [Flags] они могут использоваться в побитовых операциях (не связанных непосредственно с вопросом, но особенно полезных при описании двоичных литералов). Другой интересной возможностью является принудительное использование типа enum как встроенного (в этом примере добавьте ': byte' после 'Days'); см. встроенные типы в bit.ly/MKv42E
caligari
Это на самом деле очень легко прочитать, хотя объявление флага 0 в Enum - довольно плохая практика
MikeT
5
@MikeT: Можете ли вы уточнить (или предоставить ссылку, которая развивает) эту последнюю мысль? Я часто объявляю перечисления, которые начинаются с «1», потому что я хочу иметь возможность обнаруживать и быстро сбой, когда значение просто никогда не инициализируется. Но в некоторых ситуациях значение по умолчанию действительно имеет смысл, и я думаю, что не было бы ничего плохого в добавлении имени для этого значения.
StriplingWarrior
3
@MikeT От ca1008 : «Если перечисление, к которому применен атрибут FlagsAttribute, определяет элемент с нулевым значением, его имя должно быть« Нет », чтобы указать, что в перечислении не было задано никаких значений». Поэтому совершенно правильно добавить его в качестве значения по умолчанию, если оно называется «Нет». В целом, мне кажется, что значение в инициализированном по умолчанию поле действительно имеет имя для отображения.
Nyerguds
115

Боюсь, только целое и шестнадцатеричное напрямую (ECMA 334v4):

9.4.4.2 Целочисленные литералы Целочисленные литералы используются для записи значений типов int, uint, long и ulong. Целочисленные литералы имеют две возможные формы: десятичная и шестнадцатеричная.

Для разбора вы можете использовать:

int i = Convert.ToInt32("01101101", 2);
Марк Гравелл
источник
4
В качестве обновления к этому ответу, с новейшей современной информацией (июль 2014 года); C # 6.0 вводит двоичные литералы. Смотрите мой обновленный ответ wayyyyyy внизу ...
BTownTKD
1
@BTownTKD ваш ответ на самом деле вверху :), так как это принятый ответ.
RBT
25

В дополнение к ответу @ 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 использовать шестнадцатеричные литералы. Если вы работаете с двоичными числами достаточно часто, чтобы это вызывало беспокойство, стоит потратить время на знакомство с шестнадцатеричным.

Если вам нужно увидеть двоичные числа в исходном коде, я предлагаю добавить комментарии с двоичными литералами, как у меня выше.

Рой Тинкер
источник
23

Вы всегда можете создать квази-литералы, константы, которые содержат значение, которое вы ищете:

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

Если вы используете их часто, вы можете обернуть их в статический класс для повторного использования.

Однако, немного не по теме, если у вас есть какая-либо семантика, связанная с битами (известная во время компиляции), я бы предложил вместо этого использовать Enum:

enum Flags
{ 
    First = 0,
    Second = 1,
    Third = 2,
    SecondAndThird = 3
}
// later ...
Debug.Assert((Flags.Second | Flags.Third) == Flags.SecondAndThird);
Маркус Джонссон
источник
18

Если вы посмотрите на статус реализации языковой функции .NET Compiler Platform («Roslyn»), вы можете ясно увидеть, что в C # 6.0 это запланированная функция, поэтому в следующем выпуске мы сможем сделать это обычным способом.

Двоичный буквальный статус

totymedli
источник
3
Это случилось? Не все функции, о которых было написано в блоге, на самом деле сделали выпуск Visual Studio 2015.
полковник Паник
4
@ColonelPanic - Данэйшн указывает, что это «сейчас в списке C # 7» - github.com/dotnet/roslyn/issues/2136
Вай Ха Ли
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, извлекается из таблицы, в хотя бы статично, но все же ... Я использую C # для большого количества графического кодирования PIC и часто использую 0b101010 в Hi-Tech C

Пример из кода

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. Но 30 марта 2016 года 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 становится немного сложнее, потому что это машинный порядок; а на вашем стандартном Intel это означает little-endian. Большинству людей трудно читать буквально-порядковые литералы ...
Марк Гравелл
1
Ой, теперь я помню, почему я всегда знал, но никогда не использовал BitConverter ...
Майкл Стум
4
BitConverter имеет поле BitConverter.IsLittleEndian, которое вы можете использовать для проверки (и реверсирования буфера), если на хост-машине не существует порядка байтов.
лисица
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 просто напрашивается на неприятности.
Климент
1

Вы можете использовать 0b000001начиная с Visual Studio 2017 (C # 7.0)

Xiaoyuvax
источник
0

В принципе, я думаю, что ответ НЕТ, простого пути нет. Используйте десятичные или шестнадцатеричные константы - они просты и понятны. Ответ @RoyTinkers тоже хорош - используйте комментарий.

int someHexFlag = 0x010; // 000000010000
int someDecFlag = 8;     // 000000001000

Другие ответы здесь представляют несколько полезных рабочих раундов, но я думаю, что они не лучше, чем простой ответ. Разработчики языка C #, вероятно, сочли префикс '0b' ненужным. HEX легко преобразовать в двоичный код, и большинству программистов все равно придется знать DEC-эквиваленты 0-8.

Также при проверке значений в отладчике они будут отображаться как HEX или DEC.

RaoulRubin
источник