Чому переліки прапорців зазвичай визначаються шістнадцятковими значеннями


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якщо використовувати десяткові числа? Тим більше, що це бінарні числа, з якими ми маємо справу, а шістнадцятковий тривіально перетворюється в / з двійковій? 0x111набагато менше прикро перекладати в голові, ніж 273...
cHao

4
Прикро, що у C # немає синтаксису, який не вимагає явного виписування повноважень двох.
Полковник Паніка

Ви тут робите щось безглуздо. Завданням прапорів є те, що вони будуть розрядно поєднані. Але побітні комбінації не є елементами типу. Значення Flag1 | Flag23, а 3 не відповідає жодному доменному значенню MyEnum.
Каз

Де ви це бачите? з рефлектором?
giammin

@giammin Це загальне питання, а не конкретна реалізація. Наприклад, ви можете взяти проекти з відкритим кодом або просто код, доступний у мережі.
Аді Лестер

Відповіді:


183

Обгрунтування може відрізнятися, але перевага, яку я бачу, полягає в тому, що шістнадцятковий нагадує вам: "Гаразд, ми вже не маємо справу з числами у довільному світі, придуманому людиною, базовим десятьм. Ми маємо справу з бітами - світовим апаратом - і ми грати за його правилами. " Шістнадцятковий використовується дуже рідко, якщо ви не маєте питань щодо відносно низьких рівнів, де важливе розташування даних пам'яті. Використання цього натякає на той факт, що зараз у цій ситуації.

Крім того, я не впевнений у 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 з цим позначенням ви ніколи не помиляєтесь у обчисленні значення перерахунків
Сергій Березовський,

1
@AllonGuralnek: Чи призначає компілятор унікальні бітові позиції з анотацією [Flags]? Як правило, він починається з 0 і йде з кроком 1, тому будь-яке значення перерахунку, призначене 3 (десяткове), буде 11 у двійкових, задаючи два біти.
Ерік Дж.

2
@Eric: Так, не знаю чому, але я завжди був впевнений, що він присвоює значення двох повноважень. Я щойно перевірив, і, мабуть, помилявся.
Аллон Гуралнек

38
Ще один цікавий факт із x << yпозначенням. 1 << 10 = KB, 1 << 20 = MB, 1 << 30 = GBІ так далі. Це дуже приємно, якщо ви хочете створити масив 16 Кб для буфера, ви можете просто пітиvar buffer = new byte[16 << 10];
Скотт Чемберлен

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
Я фактично додаю 0 перед 0x1, 0x2, 0x4, 0x8 ... Тож я отримую 0x01, 0x02, 0x04, 0x08 та 0x10 ... Мені це легше читати. Я щось плутаю?
LightStriker

4
@Light - зовсім не так. Це дуже часто, тому ви можете бачити, як вони вирівнюються. Просто робить шматочки більш явними :)
Одід

2
@LightStriker просто збирається викинути його там, що має значення, якщо ви не використовуєте hex. Значення, що починаються лише з нуля, трактуються як вісімкові. Так 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]ви можете використовувати побітовое І ( &) і OR ( |) оператори , щоб об'єднати прапори. У роботі з подібними двійковими значеннями майже завжди зрозуміліше використовувати шістнадцяткові значення. Це саме причина, в якій ми в першу чергу використовуємо шістнадцятковий . Кожному шістнадцятковому символу відповідає рівно одна шпилька (чотири біти). З десятковою ознакою це відображення від 1 до 4 не відповідає дійсності.


2
Можливість використання побітових операцій насправді не має нічого спільного з атрибутом прапорів.
Маттіас Нордквіст

4

Оскільки існує механічний, простий спосіб подвоїти потужність двох у шестигранному. У десятковій частині це важко. Це вимагає тривалого розмноження у вашій голові. У hex це проста зміна. Ви можете виконувати це так, як 1UL << 63ви не можете робити десятковим способом .


1
Я думаю, що це має найбільше значення. Для переліків із великим набором значень це найпростіший шлях (за можливим винятком прикладу @ Hypercube).
Аді Лестер

3

Тому що людям легше прослідкувати там, де шматочки знаходяться у прапорі. Кожна шістнадцяткова цифра може вміщувати 4-бітний двійковий код.

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

... and so on

0xF = 1111

Зазвичай ви хочете, щоб ваші прапори не перекривались бітами, найпростіший спосіб зробити і візуалізувати це, використовуючи шістнадцяткові значення для оголошення ваших прапорів.

Отже, якщо вам потрібні прапори з 16 бітами, ви будете використовувати чотиризначні шістнадцяткові значення, і таким чином ви зможете уникнути помилкових значень:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.