C#: 检查枚举值是否在一组枚举值中的最佳方法是什么?

19

假设你有以下枚举类型:

enum MyEnum {A = 0, B = 1, C = 2, D = 4, E = 8, F = 16};

现在你有一个函数,需要检查MyEnum的实例是否为C、D或F,并在满足条件时返回true。

bool IsCDF(MyEnum enumValue) 
{
  return //something slick
}

我记得有一种非常流畅的方法可以做位移并执行此操作,比一堆三元if语句更易读,但是我现在无论如何也想不起来了。

有人知道吗?


https://dev59.com/InVD5IYBdhLWcg3wGHeu#417217 - Tim Schmelter
7个回答

37
bool IsCDF(MyEnum enumValue) 
{
  return new[]{MyEnum.C, MyEnum.D, MyEnum.F}.Contains(enumValue);
}

28
如果你将它定义为 [Flags] 枚举类型,你可以为每个枚举值分配一个不同的位值(1、2、4、8、16……)。然后,你可以使用位运算来确定一个值是否是可能值集合中的一项。

因此,要检查它是否为 C、D 或 F:

bool IsCDF(MyEnum enumValue)
{
    return ((enumValue & (MyEnum.C | MyEnum.D | MyEnum.F)) != 0);
}

或者使用 HasFlag()(效率较低但更易读):

bool IsCDF(MyEnum enumValue)
{
    return enumValue.HasFlag(MyEnum.C | MyEnum.D | MyEnum.F);
}

请注意,这种方法对于值为0(在您的示例中为“A”)不起作用,您必须小心确保所有枚举值解析为唯一的比特值(即非零二次幂)。

这种方法的优点是:

  • 通常只需要单个CPU指令/周期就可执行,而进行三个单独的“if”检查将需要3个或更多指令(取决于目标平台)。
  • 您可以将要测试的值集合作为枚举值(单个整数)传递,而无需使用枚举值列表。
  • 您可以使用位运算执行许多其他有用的操作,这些操作使用普通的数字/比较方法将变得笨拙和缓慢。

便捷提示:在定义[Flags]枚举时,请使用左移(<<)使比特值更清晰(并且更难出错),尤其是对于高阶位。

[Flags]
enum MyEnum
{
    A = 1 << 0,     // Equivalent to 1
    B = 1 << 1,     // Equivalent to 2
    C = 1 << 2,     // Equivalent to 4
    D = 1 << 3,     // Equivalent to 8Big = 1 << 26,  // Equivalent to 67108864
}

是的,我也用笔和纸画了类似的东西。我想将其与BCL的ConnectionState一起使用,但遗憾的是它没有ConnectionState.Closed = 0 - 我可以将所有内容提高到2的幂,但那样就超出了“聪明”的范围了。 - George Mauer
虽然,如果我正在检查的列表不包含0,则它应该始终评估为false... - George Mauer
是的,我晚了2年 - 但我建议不要这样做,因为从长远来看是危险的。不太易读,不太干净。 - FrankB
是的,这些说你可以使用 Enum.HasFlag(),这样代码可读性更高。 - Jason Williams

5

我可能会使用Unconstrained Melody来保持代码整洁:

if (value.HasAny(MyEnum.C | MyEnum.D | MyEnum.E))
{
    ...
}

我会将"C、D或E"提取为命名常量,可能在枚举本身中,如果它有意义的话:


不幸的是,Unconstrained Melody已经不再维护,可能不是最佳选择。 - James Esh

2

这是我创建的一个扩展,它可以让你查看给定的枚举值是否在可能的枚举值列表中。

using System.Linq;
public static class ExtensionMethods
{
    public static bool IsAny<T>(this T value, params T[] choices)
        where T : Enum
    {
        return choices.Contains(value);
    }
}

用法

bool IsCDF(MyEnum enumValue) 
{
    return enumValue.IsAny(MyEnum.C, MyEnum.D, MyEnum.F);
}

2

2
Enum.HasFlag()测试的是所有标志,而不是任何一个标志。 - Suncat2000
欢迎来到Stack Exchange。这是一个问答网站,而不是链接收集站点。请在您的回答中包含相关内容,而不仅仅是指向内容所在位置的链接。该链接可以作为参考或获取更多信息的补充。有关更多提示,请参见《如何回答》。 - Our Man in Bananas

1

也许这个扩展类对你有用:

public static class Flags
{
    /// <summary>
    /// Checks if the type has any flag of value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool HasAny<T>(this System.Enum type, T value)
    {
        try
        {
            return (((int) (object) type & (int) (object) value) != 0);
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Checks if the value contains the provided type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool Has<T>(this System.Enum type, T value)
    {
        try
        {
            return (((int)(object)type & (int)(object)value) == (int)(object)value);
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Checks if the value is only the provided type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool Is<T>(this System.Enum type, T value)
    {
        try
        {
            return (int)(object)type == (int)(object)value;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Appends a value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static T Add<T>(this System.Enum type, T value)
    {
        try
        {
            return (T)(object)(((int)(object)type | (int)(object)value));
        }
        catch (Exception ex)
        {
            throw new ArgumentException(
                string.Format(
                    "Could not append value from enumerated type '{0}'.",
                    typeof(T).Name
                    ), ex);
        }
    }

    /// <summary>
    /// Appends a value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static void AddTo<T>(this System.Enum type, ref T value)
    {
        try
        {
            value = (T)(object)(((int)(object)type | (int)(object)value));
        }
        catch (Exception ex)
        {
            throw new ArgumentException(
                string.Format(
                    "Could not append value from enumerated type '{0}'.",
                    typeof(T).Name
                    ), ex);
        }
    }

    /// <summary>
    /// Removes the value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static T Remove<T>(this System.Enum type, T value)
    {
        try
        {
            return (T)(object)(((int)(object)type & ~(int)(object)value));
        }
        catch (Exception ex)
        {
            throw new ArgumentException(
                string.Format(
                    "Could not remove value from enumerated type '{0}'.",
                    typeof(T).Name
                    ), ex);
        }
    }

    /// <summary>
    /// Removes the value.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="type"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static void RemoveFrom<T>(this System.Enum type, ref T value)
    {
        try
        {
            value = (T)(object)(((int)(object)type & ~(int)(object)value));
        }
        catch (Exception ex)
        {
            throw new ArgumentException(
                string.Format(
                    "Could not remove value from enumerated type '{0}'.",
                    typeof(T).Name
                    ), ex);
        }
    }
}

0
return (enumValue & MyEnum.C == MyEnum.C) 
       || (enumValue & MyEnum.D == MyEnum.D) 
       || (enumValue & MyEnum.F == MyEnum.F);

1
我认为楼主对这种检查方式不感兴趣。 - Bala R
是的,这仍然基本上是if语句,只不过不是True/False。 - George Mauer

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