获取枚举标志的所有位的最佳方法是什么?

20

我有一个带有Flags属性的枚举。

我的问题是,我想获取所有选项的整数位掩码,而不是手动将所有位组合起来。我想这样做是为了与某些其他int字段进行比较,并且我想保护代码以防未来的开发人员向枚举中添加更多位选项。

另一件事是,我的枚举标志中的位都是手动分配的,因此我不能简单地获取下一个值并减去1。

5个回答

27

如果我正确理解了您的问题,那么这个方法应该适合您:

Enum.GetValues(typeof(Enum)).Cast<int>().Sum();

您随后可以将其转换回 typeof(Enum) 类型:

[Flags]
public enum Values
{
    Value_1 = 1,
    Value_2 = 2,
    Value_3 = 4,
    Value_4 = 8,
    Value_5 = 16,
    Value_6 = 32,
    Value_7 = 64,
    Value_8 = 128,
    Value_9 = 256
}

static void Main(string[] args)
{
    Values values = (Values)Enum.GetValues(typeof(Values)).Cast<int>().Sum();
}

在这里输入图像描述


4
仅当定义的类型中没有重复时才有效,这通常会发生在同义词或预组合的常见组合中。 - Jon Hanna
这绝对是最干净的解决方案,此外您可以获取返回的数组并进行任何其他计算,例如OR运算而不是求和。 - Winforms
不错,但只适用于基础类型为int的情况(否则转换会失败)。我建议的解决方案更通用(但对于这种情况可能有些过度设计...) - Thomas Levesque
1
@LukeH 我同意这些经常会被创建。但是如果 OP 只是想要对所有枚举值求和,他不会提出这个问题的。他应该知道,其中有一些组合标志的重复值,并希望提到某些值不应包括在总和中。这是我的想法。如果你只想要添加特定的值,那么你不会要求添加所有值。 - spinon
3
我认为这些都是合理的问题,但不属于提问者所问的领域。如果他提到了其中一些情况,我认为贬值和评论会更相关。但他提出了一个具体的问题,我提供了一个具体的答案。我认为我们不需要把答案搞得比问题还难。 - spinon
显示剩余5条评论

7
// uses a ulong to handle all possible underlying types without error
var allFlags = Enum.GetValues(typeof(YourEnumType))
                   .Cast<YourEnumType>()
                   .Aggregate((YourEnumType)0, (a, x) => a | x, a => (ulong)a);

我不能不+1,因为当“答案已添加”消息出现时,它非常接近我正在输入的内容。但是它在长枚举上失败了,所以也许应该将Cast更改为Cast<YourEnumType>? - Jon Hanna
@Jon:已修复。聚合值现在被定义为ulong类型,以便它可以处理所有可能的基础类型。(我已经将其强制转换而不是保留为YourEnumType,因为OP要求一个整数位掩码。) - LukeH

6
请看我的 Unconstrained Melody 项目,它可以对泛型方法进行限制以便于构建具有良好特性的枚举和委托。
在这种情况下,我认为您需要调用 Flags.GetUsedBits<YourEnumType>()
如果您不介意使用额外的(非常小的)库,我想 Unconstrained Melody 可以使处理标志更加方便。如果您有任何功能请求,我很乐意查看 :)

除非有人提供更好的本地解决方案,否则我将接受这个答案。 - Winforms
@Winforms:当然,你可以在自己的代码中做到我在Unconstrained中所做的一切,除了使用通用约束来限制类型参数为枚举(或委托)。那是邪恶的黑客技巧... - Jon Skeet

5
有点粗糙,但大致如此?
[Flags]
enum SomeFlags
{
    Flag1 = 1,
    Flag2 = 2,
    Flag3 = 4,
    Flag4 = 16,
    Flag5 = 32,
    Flag6 = 64
}

static void Main(string[] args)
{
    SomeFlags flags = 0;

    SomeFlags[] values = (SomeFlags[])Enum.GetValues(typeof(SomeFlags));
    Array.ForEach<SomeFlags>(values, delegate(SomeFlags v) { flags |= v; });

    int bitMask = Convert.ToInt32(flags);
}

0

这里有一种方法可以实现,使用 Jon Skeet 和 Marc Gravell 编写的 通用运算符 的思路:

void Main()
{
    Console.WriteLine(CombineAllFlags<MyEnum>()); // Prints "Foo, Bar, Baz"
}

[Flags]
public enum MyEnum
{
    Foo = 1,
    Bar = 2,
    Baz = 4
}

public static TEnum CombineAllFlags<TEnum>()
{
    TEnum[] values = (TEnum[])Enum.GetValues(typeof(TEnum));
    TEnum tmp = default(TEnum);
    foreach (TEnum v in values)
    {
        tmp = EnumHelper<TEnum>.Or(tmp, v);
    }
    return tmp;
}

static class EnumHelper<T>
{
    private static Func<T, T, T> _orOperator = MakeOrOperator();

    private static Func<T, T, T> MakeOrOperator()
    {
        Type underlyingType = Enum.GetUnderlyingType(typeof(T));
        ParameterExpression xParam = Expression.Parameter(typeof(T), "x");
        ParameterExpression yParam = Expression.Parameter(typeof(T), "y");
        var expr =
            Expression.Lambda<Func<T, T, T>>(
                Expression.Convert(
                    Expression.Or(
                        Expression.Convert(xParam, underlyingType),
                        Expression.Convert(yParam, underlyingType)),
                    typeof(T)),
                xParam,
                yParam);
        return expr.Compile();
    }

    public static T Or(T x, T y)
    {
        return _orOperator(x, y);
    }   
}

这段代码动态创建了一个委托,使用OR运算符将枚举值组合在一起。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接