繁体   English   中英

C#:检查一组枚举值的最佳方法?

[英]C#: Best way to check against a set of Enum Values?

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

在某些时候,您有一个 function 将检查 MyEnum 的实例,如果它是 C、D 或 F,则返回 true

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

我记得有一些非常巧妙的方法可以进行位移和执行这个操作,它比一堆三元 if 语句更好读,但是对于我的生活,我不记得它是什么。

有人知道吗?

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

如果将其设为[Flags]枚举,则可以为每个枚举值分配不同的位值(1,2,4,8,1 ...)。 然后,您可以使用按位运算来确定值是否是一组可能值中的一个。

所以,看看它是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'),并且必须注意所有枚举值都会解析为唯一位值(即非零幂的2)。

这种方法的优点是:

  • 它通常需要执行一个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 8
    …
    Big = 1 << 26,  // Equivalent to 67108864
}

我可能会使用无约束旋律作为保持整洁的一种方式:

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

我可能会将“C,D或E”位提取到一个命名常量中 - 可能在枚举本身中,如果它有意义:

可能你在考虑FlagsAttribute 这里这里看一些例子。

您可以使用Enum.HasFlag方法

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

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

这是我创建的一个扩展,它允许您查看给定的 Enum 值是否在 Enum 值的可能选择的变量列表中。

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);
}
return (enumValue & MyEnum.C == MyEnum.C) 
       || (enumValue & MyEnum.D == MyEnum.D) 
       || (enumValue & MyEnum.F == MyEnum.F);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM