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

Тестирование поразрядных значений Enum

Я раньше не использовал побитовое перечисление, и я просто хочу убедиться, что мое тестирование верное. Меня больше всего интересует тестирование значений None и All. Мы получаем данные из веб-службы, которая использует это перечисление для категоризации определенных частей данных. Учитывая это, я предполагаю, что ничтожное Нет или Все никогда не будут объединены ни с какой другой ценностью.

С учетом следующего поразрядного определения перечисления:

[System.FlagsAttribute()]
public enum TrainingComponentTypes : int
    {
        None = 0,
        AccreditedCourse = 1,
        Qualification = 2,
        Unit = 4,
        SkillSet = 8,
        UnitContextualisation = 16,
        TrainingPackage = 32,
        AccreditedCourseModule = 64,
        All = 127,
    }

Я прочитал следующую цитату на этот сайт MSDN относительно FlagAttributes;

Использовать None как имя флага перечислимая константа, значение которой нуль. Вы не можете использовать None перечислимая константа в побитом И чтобы проверить флаг, потому что результат всегда равен нулю. Однако, вы можете выполнить логическое, а не побитовое сравнение между числовое значение и Нет перечислены константу, чтобы определить, в числовом значении.

Является ли логическое сравнение в этом случае ссылкой на обычный тест равенства для перечислений? Например:

TrainingComponentTypes tct = TrainingComponentTypes.None; 
if (tct == TrainingComponentTypes.None) 
{ ... }

Для побитового сравнения я выполняю следующее:

 TrainingComponentTypes tct = TrainingComponentTypes.AccreditedCourse | TrainingComponentTypes.Qualification | TrainingComponentTypes.TrainingPackage;
 Assert.IsTrue((tct & TrainingComponentTypes.AccreditedCourse) == TrainingComponentTypes.AccreditedCourse, "Expected AccreditedCourse as part the enum");

 Assert.IsFalse((tct & TrainingComponentTypes.SkillSet) == TrainingComponentTypes.SkillSet, "Found unexpected SkillSet as part the enum");

Наконец, при тестировании для всех я пробовал как логическое, так и побитовое сравнение, и оба они возвращают то же самое. Должен ли я использовать один над другим здесь? Например:

TrainingComponentTypes tct = TrainingComponentTypes.All;

Assert.IsTrue((tct & TrainingComponentTypes.All) == TrainingComponentTypes.All, "Expected All as part the enum");
Assert.IsTrue((tct) == TrainingComponentTypes.All, "Expected All as part the enum");
// The follow also pass the assertion for a value of All
Assert.IsTrue((tct & TrainingComponentTypes.Qualification) == TrainingComponentTypes.Qualification, "Expected Qualification as part the enum");
Assert.IsTrue((tct & TrainingComponentTypes.TrainingPackage) == TrainingComponentTypes.TrainingPackage, "Expected TrainingPackage as part the enum");

Итак, в общем, я хотел бы знать следующее о побитом enums;

  • Мое понимание логического сравнение правильное с учетом моего примера выше?
  • Я так понимаю поразрядное сравнение правильное?
  • Каков правильный способ обработки "Все", значение (побитовое или логическое). Я не уверен, что когда-нибудь получим ценность, когда All был объединен с другими TrainingComponentTypes. Я не понимаю, почему мы хотели бы, но тогда вы никогда не знаете?
  • Правильно ли я полагаю, что переключатель в сущности, не должно быть используется для побитовых перечислений (при условии, что нет представляется особым случаем и требует логического сравнения)?

Спасибо, Крис

4b9b3361

Ответ 1

Короткий ответ: Да:)

дольше:

1) Все операции выполняются по целочисленному значению переменной flags, поэтому вы можете думать о них в терминах этого.

2) Да.

3) Либо работает. Однако стоит отметить, что если кто-то запустит недопустимое значение в переменную, то версия == TrainingComponentTypes.All завершится с ошибкой. Например:

var badValue = (TrainingComponentTypes)128 | TrainingComponentTypes.All;
// now badValue != TrainingComponentTypes.All
// but (badValue & TrainingComponentTypes.All) == TrainingComponentTypes.All

Для этой части:

Я не уверен, что когда-нибудь получим значение, в котором все было объединено с другими тегами TrainingComponentType.

Я не уверен, что вы полностью понимаете, как enum работает под обложками.

The value of All is:
    127 = 1111111 (binary)

The other values are:
    AccreditedCourse       = 0000001
    Qualification          = 0000010
    Unit                   = 0000100
    SkillSet               = 0001000
    UnitContextualisation  = 0010000
    TrainingPackage        = 0100000
    AccreditedCourseModule = 1000000

Как вы можете видеть, все это просто побитовое значение | всех этих значений. Вы не можете комбинировать любые другие TraningComponentTypes со всеми, потому что All уже включает их! Кроме того, если вы объедините их все вместе с | самостоятельно, это будет точно так же, как и с использованием All напрямую (так что все просто удобство, когда вы определяете его внутри перечисления).

4) Вы можете использовать его для проверки None или All, но не для других значений.

Стоит отметить, что в Enum есть метод удобства, который будет выполнять эти проверки для вас: Enum.HasFlag.

Ответ 2

Правильно ли я понимаю логическое сравнение с моим примером выше?

Да, логично в этом контексте означает операторы равенства и неравенства.

Правильно ли я выполняю побитовое сравнение?

Да, но есть более простой способ: Enum.HasFlag. Например:

tct.HasFlag(TrainingComponentTypes.Qualification)

вместо:

(tct & TrainingComponentTypes.Qualification) == TrainingComponentTypes.Qualification

Каков правильный способ обработки значения "Все" (побитовое или логическое). Я не уверен, что когда-нибудь получим ценность, когда All был объединен с другими TrainingComponentTypes. Я не понимаю, почему мы хотели бы, но тогда вы никогда не знаете?

Я думаю, что лучше определить All в самом enum как побитовое ИЛИ всех его частей. Но вы увидите, что люди делают это в обоих направлениях.

Правильно ли я полагаю, что инструкции switch в основном не должны использоваться для поразрядных перечислений (если не указано, что это особый случай и требует логического сравнения)?

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

Ответ 3

  • Да.

  • Да

  • Можно использовать как логические, так и побитовые. Использование зависит от того, установлены ли все биты или только побитовое ИЛИ всех значений, которые вы определили.

  • Да, но не из-за None. Коммутатор сравнивает одно значение, тогда как поле бит может иметь несколько значений.

Как отмечали другие, Enum содержит HasFlag().

Ответ 4

1 и 2 - да, однако есть способ облегчить чтение:

TrainingComponentTypes tct = TrainingComponentTypes.AccreditedCourse | TrainingComponentTypes.Qualification;
Assert.IsTrue(tct.HasFlag(TrainingComponentTypes.AccreditedCourse), "Expected AccreditedCourse as part the enum");

3 - Я не уверен, нужна ли вам всякая ценность. Я бы удалил его.

4 - Да, оператор switch обычно не имеет смысла для перечислений флагов.

Ответ 5

"3. Каков правильный способ обработки значения" Все "(побитовое или логическое). Я не уверен, получилось ли когда-нибудь значение, в котором все было объединено с другими тегами TrainingComponentType. Я не понимаю, почему мы бы, но"

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

None = 0,
Аккредитованный курс = 1,
Квалификация = 10,
Единица = 100,
SkillSet = 1000,
UnitContextualisation = 10000,
TrainingPackage = 100000,
AccreditedCourseModule = 1000000,
Все = 1111111

помогает ли это вам понять?

Ответ 6

1 & 2 выглядят отлично

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

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

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