Расширить Enum с помощью флаговых методов? - PullRequest
2 голосов
/ 09 февраля 2012

Я нашел хорошие примеры того, как создавать методы расширения для считывания отдельных значений из побитовых перечислений. Но теперь, когда C # 4 добавил метод HasFlag, они действительно не нужны.

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

enumVariable.SetFlag(EnumType.SingleFlag, true);

ИЛИ возможно:

enumVariable.SetFlag<EnumType>(EnumType.SingleFlag, true);

Ответы [ 5 ]

3 голосов
/ 06 февраля 2014

Я сделал что-то, что работает для меня и очень просто.Вероятно, неэффективно из-за использования динамического приведения.Но, возможно, вам это понравится?

public static T SetFlag<T>(this Enum value, T flag, bool set)
{
    Type underlyingType = Enum.GetUnderlyingType(value.GetType());

    // note: AsInt mean: math integer vs enum (not the c# int type)
    dynamic valueAsInt = Convert.ChangeType(value, underlyingType);
    dynamic flagAsInt = Convert.ChangeType(flag, underlyingType);
    if (set)
    {
        valueAsInt |= flagAsInt;
    }
    else
    {
        valueAsInt &= ~flagAsInt;
    }

    return (T)valueAsInt;
}
3 голосов
/ 04 мая 2012

Сегодня я нашел решение на http://hugoware.net/blog/enums-flags-and-csharp. Спасибо, Хьюго! Отличный код, который отлично работает. Я немного подкорректировал его и добавил в существующий EnumExtender:

public static class EnumExtender
{
    /// <summary>
    /// Adds a flag value to enum.
    /// Please note that enums are value types so you need to handle the RETURNED value from this method.
    /// Example: myEnumVariable = myEnumVariable.AddFlag(CustomEnumType.Value1);
    /// </summary>
    public static T AddFlag<T>(this Enum type, T enumFlag)
    {
        try
        {
            return (T)(object)((int)(object)type|(int)(object)enumFlag);
        }
        catch(Exception ex)
        {
            throw new ArgumentException(string.Format("Could not append flag value {0} to enum {1}",enumFlag, typeof(T).Name), ex);
        }
    }

    /// <summary>
    /// Removes the flag value from enum.
    /// Please note that enums are value types so you need to handle the RETURNED value from this method.
    /// Example: myEnumVariable = myEnumVariable.RemoveFlag(CustomEnumType.Value1);
    /// </summary>
    public static T RemoveFlag<T>(this Enum type, T enumFlag)
    {
        try
        {
            return (T)(object)((int)(object)type & ~(int)(object)enumFlag);
        }
        catch (Exception ex)
        {
            throw new ArgumentException(string.Format("Could not remove flag value {0} from enum {1}", enumFlag, typeof(T).Name), ex);
        }
    }

    /// <summary>
    /// Sets flag state on enum.
    /// Please note that enums are value types so you need to handle the RETURNED value from this method.
    /// Example: myEnumVariable = myEnumVariable.SetFlag(CustomEnumType.Value1, true);
    /// </summary>
    public static T SetFlag<T>(this Enum type, T enumFlag, bool value)
    {
        return value ? type.AddFlag(enumFlag) : type.RemoveFlag(enumFlag);
    }

    /// <summary>
    /// Checks if the flag value is identical to the provided enum.
    /// </summary>
    public static bool IsIdenticalFlag<T>(this Enum type, T enumFlag)
    {
        try
        {
            return (int)(object)type == (int)(object)enumFlag;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Convert provided enum type to list of values.
    /// This is convenient when you need to iterate enum values.
    /// </summary>
    public static List<T> ToList<T>()
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException();
        var values = Enum.GetNames(typeof(T));
        return values.Select(value => value.ToEnum<T>()).ToList();
    }

    /// <summary>
    /// Present the enum values as a comma separated string.
    /// </summary>
    public static string GetValues<T>()
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException();
        var values = Enum.GetNames(typeof(T));
        return string.Join(", ", values);
    }

}
2 голосов
/ 09 февраля 2012

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

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

Вы можете передать его методу с модификатором ref, например: SetFlag(ref enumVariable, EnumType.SingleFlag), но, насколько я знаю, он не поддерживается как метод расширения.

Что вы можете сделать, это либо создать общий вспомогательный класс enum:

public static class EnumHelper
{
    public void SetFlag<TEnum>(ref TEnum enumValue, TEnum flag)
    {
         enumValue = enumValue | flag;
    }
}

или, альтернативно, создайте метод SetFlag, который возвращает новое значение, а не изменяет существующую переменную.

public static TEnum SetFlag<TEnum>(this TEnum enumValue, TEnum flag)
{
    return enumValue | flag;
}
2 голосов
/ 09 февраля 2012

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

enumVariable |= EnumType.SingleFlag;
2 голосов
/ 09 февраля 2012

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

public static T SetFlag<T>(this T @this, T flag, Boolean state) where T : enum { ... }

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

Решение

Поэтому ваши методы расширения должны выглядеть следующим образом:

public static MyFlag SetFlag(this MyFlag @this, MyFlag flag, Boolean state) 
{
    return state ? (@this | flag) : (@this & ~flag);
}
...