确定具有标志属性的枚举是否具有唯一的位值



请考虑以下枚举:

[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,
}

第一个具有这样的值,即任何组合都会产生唯一的位组合,而第二个不会。我需要以编程方式确定这一点。到目前为止,我只是在检查每个值是否都是 2 的幂,但由于此代码将用于使用其他人开发的枚举,因此不切实际。

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,
}

请忽略枚举派生自哪个整数类型以及值可能为负数的事实。

您可以将枚举值的按位 OR 与枚举值的算术总和进行比较:

var values = Enum.GetValues(typeof(EnumWithoutUniqueFlags))
    .OfType<EnumWithoutUniqueFlags>().Select(val => (int)val).ToArray();
bool areBitsUnique = values.Aggregate(0, (acc, val) => acc | val) == values.Sum();

编辑
正如@usr提到的,上面的代码仅适用于正值。
尽管OP要求忽略:

请忽略枚举派生自哪个整数类型以及值可能为负数的事实。

我很高兴介绍更优化的单循环方法:

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;
}

要使其适用于其他底层类型(uintlongulong),只需更改mask的类型并val变量即可。

编辑2
由于 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;
}

要是唯一的,枚举必须与其他枚举值没有共同点。按位 AND 运算可用于此目的。

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(...);
  }
}

我会将枚举转换为 uint,按位执行不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;
}

相关内容

最新更新