C #: Лучший способ проверить набор значений Enum? - PullRequest
8 голосов
/ 20 сентября 2011

предположим, у вас есть enum MyEnum {A = 0, B = 1, C = 2, D = 4, E = 8, F = 16};

В какой-то момент у вас есть функция, которая проверяет экземпляр MyEnum и возвращает true, если это C, D или F

bool IsCDF(MyEnum enumValue) 
{
  return //something slick
}

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

Кто угоднознать?

Ответы [ 6 ]

20 голосов
/ 20 сентября 2011
bool IsCDF(MyEnum enumValue) 
{
  return new[]{MyEnum.C, MyEnum.D, MyEnum.F}.Contains(enumValue);
}
18 голосов
/ 20 сентября 2011

Если вы сделаете перечисление [Flags], вы можете назначить различное битовое значение (1, 2, 4, 8, 16 ...) для каждого перечисляемого значения.Затем вы можете использовать побитовую операцию, чтобы определить, является ли значение одним из набора возможных значений.

Итак, чтобы увидеть, является ли оно C, D или F:

bool IsCDF(MyEnum enumValue)
{
    return ((enumValue & (MyEnum.C | MyEnum.D | MyEnum.F)) != 0);
}

или используя HasFlag() (менее эффективно, но более читабельно):

bool IsCDF(MyEnum enumValue)
{
    return enumValue.HasFlag(MyEnum.C | MyEnum.D | MyEnum.F);
}

Обратите внимание, что это не будет работать для значения 0 (в вашем примере, 'A'), и вы должны быть осторожны, чтобы всезначения enum преобразуются в уникальные битовые значения (т. е. ненулевые степени двойки).

Преимущества этого подхода следующие:

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

Полезный совет: При определении перечислений [Flags] используйте сдвиг влево (<<), чтобы сделать значения битов более четкими (и намного сложнее ошибиться), особенно для битов более высокого порядка:

[Flags]
enum MyEnum
{
    A = 1 << 0,     // Equivalent to 1
    B = 1 << 1,     // Equivalent to 2
    C = 1 << 2,     // Equivalent to 4
    D = 1 << 3,     // Equivalent to 8
    …
    Big = 1 << 26,  // Equivalent to 67108864
}
5 голосов
/ 20 сентября 2011

Я бы, возможно, использовал Неограниченную мелодию как способ поддержания порядка:

if (value.HasAny(MyEnum.C | MyEnum.D | MyEnum.E))
{
    ...
}

Я бы, вероятно, извлек бит "C, D или E" в именованныйконстанта - возможно, в самом перечислении, если оно имеет значение:

2 голосов
/ 20 сентября 2011

Может быть, вы думаете о FlagsAttribute . Посмотрите на здесь и здесь для некоторых примеров.

Вы можете использовать Метод Enum.HasFlag

1 голос
/ 20 сентября 2011

Может быть этот класс расширения полезен для вас:

public static class Flags
{
    /// <summary>
    /// Checks if the type has any flag of value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool HasAny<T>(this System.Enum type, T value)
    {
        try
        {
            return (((int) (object) type & (int) (object) value) != 0);
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Checks if the value contains the provided type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool Has<T>(this System.Enum type, T value)
    {
        try
        {
            return (((int)(object)type & (int)(object)value) == (int)(object)value);
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Checks if the value is only the provided type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool Is<T>(this System.Enum type, T value)
    {
        try
        {
            return (int)(object)type == (int)(object)value;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Appends a value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static T Add<T>(this System.Enum type, T value)
    {
        try
        {
            return (T)(object)(((int)(object)type | (int)(object)value));
        }
        catch (Exception ex)
        {
            throw new ArgumentException(
                string.Format(
                    "Could not append value from enumerated type '{0}'.",
                    typeof(T).Name
                    ), ex);
        }
    }

    /// <summary>
    /// Appends a value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static void AddTo<T>(this System.Enum type, ref T value)
    {
        try
        {
            value = (T)(object)(((int)(object)type | (int)(object)value));
        }
        catch (Exception ex)
        {
            throw new ArgumentException(
                string.Format(
                    "Could not append value from enumerated type '{0}'.",
                    typeof(T).Name
                    ), ex);
        }
    }

    /// <summary>
    /// Removes the value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static T Remove<T>(this System.Enum type, T value)
    {
        try
        {
            return (T)(object)(((int)(object)type & ~(int)(object)value));
        }
        catch (Exception ex)
        {
            throw new ArgumentException(
                string.Format(
                    "Could not remove value from enumerated type '{0}'.",
                    typeof(T).Name
                    ), ex);
        }
    }

    /// <summary>
    /// Removes the value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static void RemoveFrom<T>(this System.Enum type, ref T value)
    {
        try
        {
            value = (T)(object)(((int)(object)type & ~(int)(object)value));
        }
        catch (Exception ex)
        {
            throw new ArgumentException(
                string.Format(
                    "Could not remove value from enumerated type '{0}'.",
                    typeof(T).Name
                    ), ex);
        }
    }
}
0 голосов
/ 20 сентября 2011
return (enumValue & MyEnum.C == MyEnum.C) 
       || (enumValue & MyEnum.D == MyEnum.D) 
       || (enumValue & MyEnum.F == MyEnum.F);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...