枚举标志属性C#

Enum flag attribute C#

本问题已经有最佳答案,请猛点这里访问。

我找了一些相同的话题,但找不到我要找的。我应该使用flag enum flag attribute并检查我的数据是否在此枚举的集合中例如,枚举:

1
2
3
4
5
6
7
8
9
10
11
12
13
[Flags]
private enum MyEnum {
Apple,
Orange,
Tomato,
Potato
Melon,
Watermelon,

Fruit = Apple | Orange,
Vegetable = Tomato | Potato,
Berry = Melon | Watermelon,
}

在这个方法中,我应该检查输入数据。我该怎么做?

1
2
3
4
5
private void Checking(string data){
if(MyEnum.Fruit contains data) MessageBox.Show("Fruit");
if(MyEnum.Vegetable contains data) MessageBox.Show("Vegetables");
if(MyEnum.Berry contains data) MessageBox.Show("Berry");
}

什么应该代替"包含数据"?

更新

1
2
3
private void ZZZ(){
Cheching("Apple");
}


首先,您需要使用-2的幂序列手动对值进行编号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Flags]
private enum MyEnum
{
  None = 0,   // often useful
  Apple = 1,
  Orange = 2,
  Tomato = 4,
  Potato = 8,
  Melon =  16,
  Watermelon = 32,

  Fruit = Apple | Orange,
  Vegetable = Tomato | Potato,
  Berry = Melon | Watermelon,
}

[Flags]属性不是严格必需的,它只控制ToString()行为。

要检查字符串是否与您的值匹配,您必须首先将其设置为枚举:

1
2
3
4
5
6
7
private void Checking(string data)
{      
    MyEnum v = (MyEnum) Enum.Parse(typeof(MyEnum), data);

    if((MyEnum.Fruit & v) != 0) MessageBox.Show("It's a Fruit");
    ...
}

但是请注意,这样的枚举和字符串与Parse()之间的交换是有限的。


您还可以使用Enum类的HasFlag方法。正如Henk指出的那样,需要使用2次幂序列的值手动为枚举赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Flags]
private enum MyEnum
{
    Apple = 1,
    Orange = 2,
    Tomato = 4,
    Potato = 8,
    Melon  16,
    Watermelon = 32,

    Fruit = Apple | Orange,
    Vegetable = Tomato | Potato,
    Berry = Melon | Watermelon,
}

然后,为了检查,可以使用以下方法,该方法适用于枚举的所有组成部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void Cheking(string data)
{
    // Get the enum value of the string passed to the method
    MyEnum myEnumData;
    if (Enum.TryParse<MyEnum>(data, out myEnumData))
    {
        // If the string was a valid enum value iterate over all the value of
        // the underlying enum type
        var values = Enum.GetValues(typeof(MyEnum)).OfType<MyEnum>();
        foreach (var value in values)
        {
            // If the value is not a power of 2 it is a composed one. If it furthermore
            // has the flag passed to the method this is one we searched.
            var isPowerOfTwo = (value != 0) && ((value & (value - 1)) == 0);
            if (!isPowerOfTwo && value.HasFlag(myEnumData))
            {
                MessageBox.Show(value.ToString());
            }
        }
    }
    // In case an invalid value had been passed to the method
    // display an error message.
    else
    {
        MessageBox.Show("Invalid Value");
    }
}

或者使用LINQ以更短的方式编写它:

1
2
3
4
5
var results = Enum.GetValues(typeof(MyEnum))
                  .OfType<MyEnum>()
                  .Select(x => new { Value = x, IsPowerOfTwo = (x != 0) && ((x & (x - 1)) == 0) } )
                  .Where(x => !x.IsPowerOfTwo && x.Value.HasFlag(myEnumData))
                  .Select(x => x.Value.ToString());

这将得到一个包含结果的IEnumerable。如果myEnumData的值为MyEnum.Apple,结果将仅包含"Fruit"的值。


除了Henk Holterman的解决方案,您还可以使用扩展方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[Flags]
private enum MyEnum {
  None = 0,
  Apple = 1,
  Orange = 2,
  Tomato = 4,
  Potato = 8,
  Melon = 16,
  Watermelon = 32,

  Berry = Melon | Watermelon,
  Fruit = Apple | Orange,
  Vegetable = Potato | Tomato
}

private static class MyEnumExtensions {
  public static Boolean IsFruit(this MyEnum value) {
    return (value & MyEnum.Fruit) == MyEnum.Fruit;
  }

  public static Boolean IsVegetable(this MyEnum value) {
    return (value & MyEnum.Vegetable) == MyEnum.Vegetable;
  }

  public static Boolean IsBerry(this MyEnum value) {
    return (value & MyEnum.Berry) == MyEnum.Berry;
  }
}

...

MyEnum data = ...

if (data.IsBerry()) {
  MessageBox.Show("Berry");      
}

正如@henk holterman所建议的,您需要首先为枚举赋值。所有值应为2的幂(并避免使用0,除非"无"的特殊情况)它应该是这样的:

1
2
 MyEnum eVal= (MyEnum ) Enum.Parse( typeof(MyEnum), data, true );
 if((MyEnum.Fruit & eVal) != 0) MessageBox.Show("Fruit");

您可能想阅读更多关于位代数和布尔代数的内容。