Самый простой способ "ИЛИ" всех значений в Flagged Enum? - программирование
Подтвердить что ты не робот

Самый простой способ "ИЛИ" всех значений в Flagged Enum?

С учетом enum:

[Flags]
public enum mytest
{
    a = 1,
    b = 2,
    c = 4
}

Я придумал два способа представления всех значений в одной переменной:

    var OR1 = (mytest)Enum.GetNames(typeof(mytest)).Sum(a => (int)Enum.Parse(typeof(mytest), a));
    var OR2 = (mytest)(typeof(mytest).GetEnumValues() as mytest[]).Sum(a => (int)a);

Теперь, хотя оба они работают, есть более аккуратный способ? Возможно,.NET-метод мне не хватает?

Изменить: для пояснения мне нужна функция, которая будет динамической - я не хочу ее вычислять, указав каждое значение enum.

4b9b3361

Ответ 1

Если имеет смысл иметь член All, просто укажите его непосредственно:

[Flags]
public enum mytest
{
    a = 1,
    b = 2,
    c = 4,
    All = 7
}

Хотя, более идиоматичным способом их написания может быть:

[Flags]
public enum MyTest
{
    A = 1,
    B = 1 << 0x01,
    C = 1 << 0x02,
    All = A | B | C
}

Это показывает логическую прогрессию значений перечисления, а в случае All упрощает добавление другого члена.

Ответ 2

Используйте Enumerable.Aggregate() побитовые или вместе. Это будет работать, даже если у вас есть значения перечисления, которые представляют несколько битов набора, в отличие от Sum().

var myTestValues = (MyTest[]) typeof(MyTest).GetEnumValues();
var sum = myTestValues.Aggregate((a, b) => a | b);
sum.Dump();

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

TEnum AllEnums<TEnum>() 
{
    var values = typeof(TEnum).GetEnumValues().Cast<int>();
    return (TEnum) (object) values.Aggregate((a,b) => a|b);
}

Ответ 3

Для общего метода используйте Linq Enumerable.Aggregate метод расширения;

var flags = Enum.GetValues(typeof(mytest))
                .Cast<int>()
                .Aggregate(0, (s, f) => s | f);

Или в методе обертки

TEnum GetAll<TEnum>() where TEnum : struct
{
    return (TEnum) (object)
            Enum.GetValues(typeof(TEnum))
                .Cast<int>()
                .Aggregate(0, (s, f) => s | f);
}

полный кредит за этот трюк с двойным броском переходит на @millimoose

Ответ 4

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

mytest allValues = (mytest)int.MaxValue;

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

Ответ 5

Как насчет чего-то вроде

var all = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>().Last() * 2 - 1;

в основном

all = max*2-1

это работает, только если все значения присутствуют от 1 до максимального значения.

1,2,4... 32,64...

Ответ 6

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

static public TEnum GetAllFlags<TEnum>() where TEnum : struct, IComparable, IFormattable, IConvertible
    {
        unchecked
        {
            if (!typeof(TEnum).IsEnum)
                throw new InvalidOperationException("Can't get flags from non Enum");
            object val = null;
            switch (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(TEnum))))
            {
                case TypeCode.Byte:
                case TypeCode.SByte:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<Byte>()
                                .Aggregate(default(Byte), ( s, f) => (byte)(s | f));
                    break;
                case TypeCode.Int16:
                case TypeCode.UInt16:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt16>()
                                .Aggregate(default(UInt16), ( s, f) => (UInt16)(s | f));
                    break;
                case TypeCode.Int32:
                case TypeCode.UInt32:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt32>()
                                .Aggregate(default(UInt32), ( s, f) => (UInt32)(s | f));
                    break;
                case TypeCode.Int64:
                case TypeCode.UInt64:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt64>()
                                .Aggregate(default(UInt64), ( s, f) => (UInt64)(s | f));
                    break;
                default :
                    throw new InvalidOperationException("unhandled enum underlying type");

            }
            return (TEnum)Enum.ToObject(typeof(TEnum), val);
        }
    }

Подробнее об этом виде конверсий можно найти здесь