【问题标题】:Determine if an Enum with FlagsAttribute has unique bit values确定具有 FlagsAttribute 的枚举是否具有唯一的位值
【发布时间】:2015-02-02 04:33:56
【问题描述】:

考虑以下枚举:

[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 的幂,但是由于这段代码将用于消耗其他人开发的 Enum,因此并不实用。

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

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

【问题讨论】:

  • 一个标志枚举定义的值不是 2 的幂是常见的 - 命名为以 2 的幂定义的状态的叠加的状态可能很有用。

标签: c# .net enums bit-manipulation


【解决方案1】:

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

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),只需更改 maskval 变量的类型。

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

【讨论】:

  • 不适用于以下输入:new int[4]{ -98272, 65564, -98240, 129410 }。由智能单元测试(也称为 Microsoft Pex)确定。它似乎适用于所有积极的价值观。如果允许负值,则至少需要三个才能找到错误。
  • @usr 非常感谢。请修改我编辑的帖子。
  • @Dmitry:枚举中的一个或多个值是否为零有关系吗?
  • @RaheelKhan 枚举中的零值不会影响方法的返回值。我添加了一种检查零重复项的新方法。请修改。
【解决方案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(...);
  }
}

【讨论】:

    【解决方案3】:

    我会将枚举转换为 uint,按位而不是 0x0,使用异或,如果下一个值小于前一个值,则发生冲突

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

    【讨论】:

    • 如果枚举值是例如13 怎么办?或任何其他具有另一个公共位但不是最高位的值。
    • @Dmitry 你说得对,谢谢提醒,会努力挽救这个
    猜你喜欢
    • 1970-01-01
    • 2014-09-19
    • 1970-01-01
    • 2022-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-05
    相关资源
    最近更新 更多