Подтвердить что ты не робот

Зачем использовать флаги + битмаски, а не последовательность логических элементов?

Учитывая случай, когда у меня есть объект, который может находиться в одном или нескольких состояниях true/false, я всегда был немного нечетким, почему программисты часто используют флаги + битмаски вместо того, чтобы просто использовать несколько логических значений.

Все это в рамках платформы .NET. Не уверен, что это лучший пример, но .NET framework имеет следующее:

public enum AnchorStyles
{
    None = 0,
    Top = 1,
    Bottom = 2,
    Left = 4,
    Right = 8
}

Поэтому, учитывая стиль привязки, мы можем использовать битмаски для определения того, какое из состояний выбрано. Однако похоже, что вы могли бы сделать то же самое с классом/структурой AnchorStyle с свойствами bool, определенными для каждого возможного значения, или массивом индивидуальных значений перечисления.

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

Итак, зачем использовать этот подход?

  • Меньше потребления памяти? (похоже, что он потребляет меньше, чем массив /struct bools).
  • Лучшая производительность стека/кучи, чем структура или массив?
  • Более быстрая операция сравнения? Более быстрое добавление/удаление значений?
  • Более удобный для разработчика, который его написал?
4b9b3361

Ответ 1

Это традиционно способ уменьшить использование памяти. Итак, да, его довольно устаревшее в С#: -)

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

Быстро сравнивать значения, хранящиеся в виде битовой маски. Используйте логические операторы AND и OR и сравните полученные 2 ints.

Он использует значительно меньше памяти. Помещение всех четырех значений вашего примера в битовую маску будет использовать половину байта. Использование массива bools, скорее всего, будет использовать несколько байтов для объекта массива плюс длинное слово для каждого bool. Если вам нужно сохранить миллион значений, вы увидите, почему именно битмаксная версия превосходит.

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

И из-за макета памяти намного быстрее в каждом аспекте, чем в массиве. Это почти так же быстро, как использование одного 32-битного целого. Мы все знаем, что это так быстро, как вы можете получить для операций с данными.

Ответ 2

  • Простота установки нескольких флагов в любом порядке.

  • Простота сохранения и получения серии 0101011 в базе данных.

Ответ 3

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

Ответ 4

Он также может сделать методы более понятными. Представьте метод с 10 bools против 1 Bitmask.

Ответ 5

Фактически, он может иметь лучшую производительность, главным образом, если ваше перечисление происходит из байта. В этом крайнем случае каждое значение перечисления будет представлено байтом, содержащим все комбинации, до 256. Имея так много возможных комбинаций с булевыми значениями, получим 256 байтов.

Но даже тогда я не думаю, что это настоящая причина. Причина, по которой я предпочитаю, - это сила С#, которая позволяет мне обрабатывать эти перечисления. Я могу добавить несколько значений с одним выражением. Я могу их удалить. Я могу даже сравнить несколько значений сразу с одним выражением, используя перечисление. С булевым кодом код может стать, скажем, более подробным.

Ответ 6

Раймонд Чен сообщение в блоге по этому вопросу.

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

Как говорили другие, его время в основном прошло. Заманчиво по-прежнему это делать, потому что бит возиться - это весело и круто, но он более неэффективен, имеет серьезные недостатки в плане обслуживания, он не играет хорошо с базами данных, и если вы не работаете в встроенный мир, у вас достаточно памяти.

Ответ 7

Я бы посоветовал никогда не использовать флаги enum, если вы не имеете дело с некоторыми довольно серьезными ограничениями памяти (маловероятно). Вы всегда должны писать код, оптимизированный для обслуживания.

Наличие нескольких булевых свойств облегчает чтение и понимание кода, изменение значений и предоставление комментариев Intellisense, не говоря уже о снижении вероятности ошибок. При необходимости вы всегда можете использовать поле флага enum внутри, просто убедитесь, что вы указали настройку/получение значений с помощью логических свойств.

Ответ 8

Это для скорости и эффективности. По сути, все, с чем вы работаете, - это один int.

if ((flags & AnchorStyles.Top) == AnchorStyles.Top)
{
    //Do stuff
} 

Ответ 9

С точки зрения модели домена, в некоторых ситуациях она лучше моделирует реальность. Если у вас есть три логических типа, таких как AccountIsInDefault и IsPreferredCustomer и RequiresSalesTaxState, то не имеет смысла добавлять их в одну нумерацию, отмеченную флажками, потому что они не являются тремя различными значениями для одного и того же элемента модели домена.

Но если у вас есть набор булеров вроде:

 [Flags] enum AccountStatus {AccountIsInDefault=1, 
         AccountOverdue=2 and AccountFrozen=4}

или

  [Flags] enum CargoState {ExceedsWeightLimit=1,  
         ContainsDangerousCargo=2, IsFlammableCargo=4, 
         ContainsRadioactive=8}

Тогда полезно иметь возможность хранить общее состояние учетной записи (или груза) в ОДНОЙ переменной..., которая представляет ОДНЫЙ элемент домена, значение которого может представлять любую возможную комбинацию состояний.

Ответ 10

  • Эффективность пространства - 1 бит
  • Эффективность времени - бит-сравнения быстро обрабатываются аппаратным обеспечением.
  • Независимость от языка - где данные могут обрабатываться рядом различных программ, вам не нужно беспокоиться о реализации булевых языков на разных языках/платформах.

В большинстве случаев, это не стоит компромисс с точки зрения технического обслуживания. Однако есть моменты, когда это полезно:

  • Сетевые протоколы - будет значительная экономия при уменьшении размера сообщений.
  • Программное обеспечение Legacy - однажды мне пришлось добавить некоторую информацию для отслеживания в какое-то унаследованное программное обеспечение.

Стоимость изменения заголовка: миллионы долларов и годы усилий. Затрать на то, чтобы вырезать информацию в 2 байта в заголовке, которые не использовались: 0.

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