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

Флаги enum & побитовые операции против "строки бит"

Другой разработчик предположил, что мы сохраняем выбор дней недели в виде 7-символьной строки из 1 и 0, т.е. "1000100" в понедельник и пятницу. Я предпочел (и настоятельно предложил) решение с перечислением Flags и побитовыми операциями, я считаю, что это более чистый способ сделать это, и это должно быть проще понять для других разработчиков.

  [Flags()]
  public enum Weekdays : int
  {
    Monday = 1,
    Tuesday = 2,
    Wednesday = 4,
    Thursday = 8,
    Friday = 16,
    Saturday = 32,
    Sunday = 64
  }

Однако, когда я начал внедрять образцовое решение, я понял, что, возможно, простой строковый подход был проще в конце концов: конечно, бит-строка более очевидна, чем "17", если вы просто смотрите на данные. И я считаю, что побитовые операции С# являются интуитивно понятными и чрезвычайно подробными:

Weekdays workDays = Weekdays.Monday | Weekdays.Tuesday;
if ((workDays & Weekdays.Monday) == Weekdays.Monday) 
{...}

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

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

  • Лучшая производительность
  • Меньше места для хранения

Итак, как мне продавать побитное решение для моих коллег? Нужно ли мне? Каковы другие преимущества использования этого метода над строками? После завершения образца проекта я обнаружил, что команда по-прежнему выбрала линейное решение. Мне нужны более хорошие/более сильные аргументы. Почему вы должны использовать перечисления флагов, а не простые битовые строки?

4b9b3361

Ответ 1

Преимущества использования перечисления флагов:

Отрицание использования перечисления флагов:

  • Представление данных для людей трудно понять (например, какие флаги установлены для 17?)


Преимущества использования строки бит:

  • Легко для программистов видеть, какие биты заданы в строке

Отрицание использования строки бит:

  • Нестандартный подход
  • Сложнее понять для программистов, незнакомых с вашим дизайном.
  • Потенциально проще установить значения "мусора" (например, stringValue = "Sunday" )
  • Создание бессмысленной строки
  • Необязательный синтаксический анализ строк
  • Дополнительные разработки
  • Возвращение колеса (но даже круглое колесо)


Насколько важно иметь возможность смотреть на строку битов, чтобы увидеть, что установлено? Если вам трудно понять, что 17 - понедельник и пятница, вы всегда можете использовать калькулятор и конвертировать в двоичный файл. Или добавьте какое-то строковое представление для "отображения" (или отладки). Это не так сложно.


Мне также кажется, что если вы собираетесь сделать строку бит приблизительной, то вам нужно будет сделать довольно много инкапсуляции, чтобы довести ее до уровня абстракции, который уже перечисляет перечисление Flags, Если подход состоит в том, чтобы просто манипулировать строкой битов напрямую, это будет трудно читать (и понимать) и, вероятно, подвержено ошибкам.

например. вы можете увидеть это:

days = "1000101"; // fixed bug where days were incorrectly set to "1010001"

Ответ 2

Вам не следует создавать нестандартные структуры данных, чтобы заменить стандартную структуру данных (в данном случае - встроенное перечисление DayOfWeek). Вместо этого расширьте существующую структуру. Это работает, по сути, так же, как метод бит-битов, о котором вы говорили.

namespace ExtensionMethods
{
    public static class Extensions
    {
        /*
         * Since this is marked const, the actual calculation part will happen at
         * compile time rather than at runtime.  This gives you some code clarity
         * without a performance penalty.
         */
        private const uint weekdayBitMask =
            1 << Monday 
            | 1 << Tuesday
            | 1 << Wednesday
            | 1 << Thursday
            | 1 << Friday;
        public static bool isWeekday(this DayOfWeek dayOfWeek)
        {
            return 1 << dayOfWeek & weekdayBitMask > 0;
        }
    }   
}

Теперь вы можете сделать следующее:

Thursday.isWeekday(); // true
Saturday.isWeekday(); // false

Ответ 3

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

Что-то вроде:

[Flags]
public enum Days {
   Monday = 1,
   Tuesday = 2,
   Wednesday = 4,
   Thursday = 8,
   Friday = 16,
   Saturday = 32,
   Sunday = 64,
   MondayToFriday = 31,
   All = 127,
   None = 0
}

public class Weekdays {

   private Days _days;

   public Weekdays(params Days[] daysInput) {
      _days = Days.None;
      foreach (Days d in daysInput) {
         _days |= d;
      }
   }

   public bool Contains(Days daysMask) {
      return (_days & daysMask) == daysMask;
   }

   public bool Contains(params Days[] daysMasks) {
      Days mask = Days.None;
      foreach (Days d in daysMasks) {
         mask |= d;
      }
      return (_days & mask) == mask;
   }

}

Пример использования:

Weekdays workdays = new Weekdays(Days.MondayToFriday);
if (workdays.Contains(Days.Monday, Days.Wednesday)) {
   ...
}

Ответ 4

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

Однако, по крайней мере, во всех приложениях, которые я когда-либо создавал, такие данные где-то попадают в крошечное поле и больше никогда не видны, кроме как через код С#, что означает, что битфлаги, безусловно, самые простые - это большинство читаемых человеком кода. Ваши коллеги действительно хотят написать синтаксический анализатор строк, который отображает 0 и 1 в значения вместо использования встроенного и используется для 40-летнего представления о побитовых операциях?

Ответ 5

Интересно, что оба этих метода точно такие же; только метод flags более очевиден.

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

- Изменить

И чтобы быть понятным, производительность, по-моему, не обязательно должна учитывать то, что вы делаете. Поэтому просто пойдите с самым читаемым. (Который, IMHO, является именованным флагом).

Ответ 6

Метод Flags является идиоматическим (т.е. тем, что опытные программисты делают и привыкли видеть и делать, по крайней мере, на языках C/С++/С#).