枚举 - 所有选项值

26

有没有一种方法可以在枚举中添加一个“所有值”选项,而不必每次添加新值时都更改它的值?

[Flags] 
public enum SomeEnum
{
    SomeValue =  1,
    SomeValue2 = 1 << 1,
    SomeValue3 = 1 << 2,
    SomeValue4 = 1 << 3,
    All = ?
}

更新:

最终采用从long类继承并使用long.MaxValue来表示全选选项。


-1 会让我对该值进行特殊处理,无法使用 HasFlag 方法。 - Gil Stal
你一般都是测试 [Flags] 枚举的离散位,因此它应该完全正常。 - Marc Gravell
实际上,我想要一个“全部选项”,无论我测试哪个选项,HasFlag都会返回true。 - Gil Stal
8个回答

43

Flags枚举类型中,应该定义空值,例如None = 0。因此,定义All值最简单的方法是通过反转None中的所有位。

[Flags]
enum MyEnum
{
   None = 0,
   A = 1,
   B = 2,
   C = 4,
   ...
   All = ~None
}
请注意,对于无符号的基础类型而言,使用~None而不是~0将无法工作,因为~None等于-1,这是无符号类型的非法值。 编辑:答案已修改为使用倒置的None而不是显式常数(例如0x7FFFFFFF或~0),因为这对于无符号仍然有效。

2
您还可以将“全部”枚举的值设置为〜0,其功能相同。我只是看到所有那些F挨在一起就感到困惑。 - Wells
14
我不同意使用“-1”、“0xFFFFFFFF”或“~0”的解决方案。当您将“All = -1”假设为真时,“(SomeEnum.SomeValue | SomeEnum.SomeValue2 | SomeEnum.SomeValue3 | SomeEnum.SomeValue4).HasFlag(SomeEnum.All)”是错误的。也许可以尝试使用“All = (SomeValue4 << 1) - 1”这样的解决方案。 - tykovec
我更新了答案,包括其他整数类型。 - Anders Forsgren
int.MaxValue 可以使用吗? - Vladius
是的,int.MaxValue(或uint.MaxValue,或ulong.MaxValue等)有效。 - Anders Forsgren
显示剩余3条评论

18

应该像这样:

[Flags] 
public enum SomeEnum
{
    SomeValue =  1,
    SomeValue2 = 1 << 1,
    SomeValue3 = 1 << 2,
    SomeValue4 = 1 << 3,
    All = SomeValue | SomeValue2 | SomeValue3 | SomeValue4
}

5
我不反对,但请注意原帖明确要求免维护选项。虽然我有点同意明确表达最好。 - Marc Gravell
我也喜欢这种明确的方法,并为解决维护问题,我会添加一个单元测试,至少警告用户如果枚举中添加了新选项而不是All OR,但可能会有另一个问题:在我的情况下,我需要跟踪用户是否明确选择了所有选项(=也应考虑将来添加的选项)或者用户逐个选择了所有现有选项(=不应考虑将来添加的选项),因此我需要使用All = ~None,因为它具有不同的值。 - Daniele Armanasco

5

枚举可以由许多不同长度的整数类型(short、int、long)组成。这使得#FFFFFFFF的解决方案不合适(正如@MarcGravell评论中指出的那样)。

枚举可以由无符号类型(如uint)组成。这使得-1的解决方案不合适。

我的最佳选择是,无需维护:

All = ~0

3
使用枚举的行为来计算最后一个值。
在所有“真实”的枚举值之后添加Last字段。
添加等于(Last << 1) - 3All字段。
[Flags]
public enum SomeEnum
{
    SomeValue =  1,
    SomeValue2 = 1 << 1,
    SomeValue3 = 1 << 2,
    SomeValue4 = 1 << 3,

    // Do not add values after this
    Last,
    All = (Last << 1) - 3,
}

我在这里回答了这个问题: 如何使用带有附加选项(全部,无)的枚举
你可以查看我的博客Enum Trick获取更多信息和想法。

你是说 All = ((Last - 1) << 1) - 1 对吧? - Jörgen Sigvardsson
实际上不是这样的...假设您有4个二进制标志。LAST = 1001 = 最后一个标志 + 1。LAST << 1 = 10010 = 下一个 标志 + 2。从中减去3,您将得到下一个标志 - 1 (10000 - 1 = 在我们的示例中为1111),即所有实际值。 - Alicia

2

不,目前没有任何内置功能可以在枚举值改变时自动更新All选项。

您可能需要一个特殊的值(监视值),表示All(例如-1),即使它不是所有选项的按位和。

另一种选择是使用一个具有所有位开启的值:

All = 0xFFFFFFFF

1
我喜欢使用-1而不是0xFFFFFFFF的原因是...现在想象一下,如果你想要改变为“short”或“long” - Marc Gravell

0

你可以使用一个小技巧

(SomeEnum)( (1 << ( Enum.GetValues( typeof(SomeEnum) ).Length ) ) -1 )

如果您添加了值为0的'None'枚举名称(None = 0,),则需要在长度之后放置'-1'。

我喜欢这个想法,但不喜欢实现方式。 - Brackets
此外,无论您是否有“None”,都需要将其设置为-1。 - Brackets

0

如果您可以接受在单独的类型中使用 static readonly 字段而不是 const 枚举字段,则可以实现此操作:

[Flags] 
public enum SomeEnum
{
    None       = 0,
    SomeValue  = 1,
    SomeValue2 = 1 << 1,
    SomeValue3 = 1 << 2,
    SomeValue4 = 1 << 3,
}

public static class SomeEnumUtility {

    private static readonly SomeEnum[] _someEnumValues = (SomeEnum[])Enum.GetValues( typeof(SomeEnum) );
    public static readonly SomeEnum SomeEnum_All = GetSomeEnumAll();

    // Unfortunately C# does not support "enum generics" otherwise this could be a generic method for any Enum type
    private static SomeEnum GetSomeEnumAll() {

        SomeEnum value = SomeEnum.None; // or `(SomeEnum)0;` if None is undefined.
        foreach(SomeEnum option in _someEnumValues) {
            value |= option;
        }
        return value;
    }
}

然后你可以得到SomeEnumUtility.SomeEnum_All。由于它是一个static readonly,计算只会在线程安全的情况下执行一次。

正如我在代码注释中所写的那样,不幸的是C#不支持枚举泛型,否则你可以这样做:

    private static TEnum GetEnumAllFlags<TEnum>() where TEnum : enum {

        TEnum[] allValues = Enum.GetValues<TEnum>();

        TEnum value = (TEnum)0;
        foreach(TEnum option in allValues) {
            value |= option;
        }
        return value;
    }

哦,好吧 :(


虽然看起来很酷,但它总是需要函数的处理。如果不重要的话,我更喜欢<EnumBaseType(like int, byte)>.MaxValue,或者NoneAll=~None选项。 - Hassan Faghihi

0
public static T EnumSetAll<T>() where T : struct, Enum
  {
    string str = string.Join(", ", Enum.GetNames(typeof(T)));

    if (Enum.TryParse<T>(str, out var e))
      return e;

    return default;
  }

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