Reputation: 14787
Consider the following enumeration:
[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,
}
The first one has values such that any combination will produce a unique bit combination while the second one will not. I need to programmatically determine this. So far I had only been checking if every value was a power of two but this since this code will be used to consume Enums developed by others, it is not practical.
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."));
}
}
As opposed to the code above how could programatically determine that the following Enum meets the bit combination uniqueness objective (without assumptions such as values being powers of 2, etc.)?
[Flags]
public enum EnumWithoutUniqueFlags
{
Two = 2,
Four = 9,
Five = 64,
}
Please ignore which integral type the enum derives from as well as the fact that values could be negative.
Upvotes: 4
Views: 724
Reputation: 14059
You could compare the bitwise OR of enum values with the arithmetic sum of enum values:
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
As the @usr mentioned, the code above works for positive values only.
Despite the fact that the OP requested to ignore:
Please ignore which integral type the enum derives from as well as the fact that values could be negative.
I'm glad to introduce more optimal single loop approach:
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;
}
To make it work for other underlying type (uint
, long
, ulong
), just change the type of the mask
and val
variables.
EDIT2
Since the Enum.GetValues
method returns values in the ascending order of their unsigned magnitude, if you need to check for duplicates of zero value, you could use the following approach:
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;
}
Upvotes: 4
Reputation: 3231
I would cast the enums to uints, do a bitwise not of a 0x0, use an xor and if the next value is less than the previous one you have a collision
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;
}
Upvotes: 0
Reputation: 35477
To be unique, the enum must have no bits in common with other enum values. The bitwise AND operation can be used for this.
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(...);
}
}
Upvotes: 2