2015-02-02 1 views
3

Рассмотрят следующий перечень:определить, если Enum с FlagsAttribute имеет уникальный бит значение

[Flags] 
public enum EnumWithUniqueBitFlags 
{ 
    None = 0, 
    One = 1, 
    Two = 2, 
    Four = 4, 
    Eight = 8, 
} 

[Flags] 
public enum EnumWithoutUniqueFlags 
{ 
    None = 0, 
    One = 1, 
    Two = 2, 
    Four = 4, 
    Five = 5, 
} 

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

var values = Enum.GetValues(typeof(TEnum)).OfType<TEnum>().ToList(); 

for (int i = 0; i < values.Count; i++) 
{ 
    if (((int) ((object) values [i])) != ((int) Math.Pow(2, i))) 
    { 
     throw (new Exception("Whatever.")); 
    } 
} 

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

[Flags] 
public enum EnumWithoutUniqueFlags 
{ 
    Two = 2, 
    Four = 9, 
    Five = 64, 
} 

Пожалуйста, проигнорируйте, какой интегральный тип имеет перечисление, а также тот факт, что значения могут быть отрицательными.

+5

Перечисление флагов, определяющее значения, которые не являются степенями двух, является общим - состояния наименования, которые являются суперпозициями состояний, определенных по степеням двух, могут быть полезны. –

ответ

4

Вы могли бы сравнить побитовое ИЛИ значений перечислений с арифметической суммой значений перечислений:

var values = Enum.GetValues(typeof(EnumWithoutUniqueFlags)) 
    .OfType<EnumWithoutUniqueFlags>().Select(val => (int)val).ToArray(); 

bool areBitsUnique = values.Aggregate(0, (acc, val) => acc | val) == values.Sum(); 

EDIT
Поскольку @usr упоминалось, приведенный выше код работает для положительных значений только ,
Несмотря на то, что ОП запрошенной игнорировать:

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

Я рад представить более оптимальный единый подход цикла:

private static bool AreEnumBitsUnique<T>() 
{ 
    int mask = 0; 
    foreach (int val in Enum.GetValues(typeof(T))) 
    { 
     if ((mask & val) != 0) 
      return false; 
     mask |= val; 
    } 
    return true; 
} 

Чтобы заставить его работать на другой базовый тип (uint, long, ulong), просто изменить тип mask и val переменных.

EDIT2
Поскольку метод Enum.GetValues возвращает значения в порядке возрастания их знак величины, если вам нужно проверить на наличии дубликатов нулевого значения, то можно использовать следующий подход:

private static bool AreEnumBitsUnique<T>() 
{ 
    int mask = 0; 
    int index = 0; 
    foreach (int val in Enum.GetValues(typeof(T))) 
    { 
     if ((mask & val) != 0) // If `val` and `mask` have common bit(s) 
      return false; 
     if (val == 0 && index != 0) // If more than one zero value in the enum 
      return false; 
     mask |= val; 
     index += 1; 
    } 
    return true; 
} 
+0

Не работает для следующих входов: 'new int [4] {-98272, 65564, -98240, 129410}'. Определяется с помощью Smart Unit Tests aka Microsoft Pex. Кажется, это работает для всех положительных ценностей.Если отрицательные значения разрешены, по крайней мере три необходимы для поиска ошибки. – usr

+0

@usr Большое спасибо. Пожалуйста, пересмотрите мой отредактированный пост. – Dmitry

+0

Вы заработали +1. – usr

2

Чтобы быть уникальным, перечисление не должно иметь битов, совпадающих с другими значениями перечисления. Для этого можно использовать bitwise AND operation.

for (int i = 0; i < values.Count; ++i) 
{ 
    for (int j = 0; j < values.Count; ++j) 
    { 
    if (i != j && ((values[i] & values[j]) != 0)) 
     throw new Exception(...); 
    } 
} 
0

Я бы перевел перечисления на uints, поразрядным образом не с 0x0, используйте xor, и если следующее значение меньше предыдущего, у вас есть столкновение

public static bool CheckEnumClashing<TEnum>() 
{ 
    uint prev = 0; 
    uint curr = 0; 


    prev = curr = ~curr; 

    foreach(var target in Enum.GetValues(typeof(TEnum)).Select(a=>(uint)a)) 
    { 
     curr ^=target; 

     if(curr <= prev) 
      return false; 

     prev = curr; 
    } 

    return true; 
} 
+0

Что делать, если значения перечисления являются, например, '1',' 3'? Или любые другие значения, имеющие другой общий бит, но не самый старший бит. – Dmitry

+0

@ Дмитрий, ты прав, спасибо за головы, попробуй и спас его – konkked

Смежные вопросы