Зачем использовать флаги + битовые маски, а не серии логических значений? - PullRequest
23 голосов
/ 10 сентября 2009

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

Это все на .NET Framework. Не уверен, что это лучший пример, но .NET Framework имеет следующее:

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

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

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

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

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

Ответы [ 10 ]

17 голосов
/ 10 сентября 2009

Традиционно это был способ сокращения использования памяти. Так что, да, это довольно устарело в C #: -)

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

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

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

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

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

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

  • Легко сохранить и получить серию 0101011 в базу данных.

7 голосов
/ 10 сентября 2009

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

6 голосов
/ 10 сентября 2009

Это также может сделать методы более понятными. Представьте себе метод с 10 бул против 1 битовой маски.

3 голосов
/ 10 сентября 2009

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

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

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

3 голосов
/ 10 сентября 2009

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

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

3 голосов
/ 10 сентября 2009

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

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

2 голосов
/ 10 сентября 2009

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

Но если у вас есть набор логических значений, таких как:

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

или

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

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

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

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

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

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

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

0 голосов
/ 10 сентября 2009

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

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