Comparing enum flags in C#

后端 未结 7 1503
囚心锁ツ
囚心锁ツ 2021-02-07 18:37

I need to detect if a flag is set within an enum value, which type is marked with the Flag attribute.

Usually it is made like that:

(value & flag) ==         


        
相关标签:
7条回答
  • 2021-02-07 19:13

    For me it looks overcomplicated. How about this (keeping in mind that enum is always mapped to an integer value type):

    public static bool IsSet<T>(T value, T flags) where T : struct
    {
        // You can add enum type checking to be perfectly sure that T is enum, this have some cost however
        // if (!typeof(T).IsEnum)
        //     throw new ArgumentException();
        long longFlags = Convert.ToInt64(flags);
        return (Convert.ToInt64(value) & longFlags) == longFlags;
    }
    
    0 讨论(0)
  • 2021-02-07 19:13

    I have used this to compare flags

    public static bool IsSet<T>(this T input, T match)
    {
        return (Convert.ToUInt32(input) & Convert.ToUInt32(match)) != 0;
    }
    

    Here you can do the different conversions. From int to short to long.

    0 讨论(0)
  • 2021-02-07 19:14

    I wrote a set of extension methods for enums, in case you need it :

    public static class EnumExtensions
    {
        private static void CheckEnumWithFlags<T>()
        {
            if (!typeof(T).IsEnum)
                throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName));
            if (!Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))
                throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName));
        }
    
        public static bool IsFlagSet<T>(this T value, T flag) where T : struct
        {
            CheckEnumWithFlags<T>();
            long lValue = Convert.ToInt64(value);
            long lFlag = Convert.ToInt64(flag);
            return (lValue & lFlag) != 0;
        }
    
        public static IEnumerable<T> GetFlags<T>(this T value) where T : struct
        {
            CheckEnumWithFlags<T>();
            foreach (T flag in Enum.GetValues(typeof(T)).Cast<T>())
            {
                if (value.IsFlagSet(flag))
                    yield return flag;
            }
        }
    
        public static T SetFlags<T>(this T value, T flags, bool on) where T : struct
        {
            CheckEnumWithFlags<T>();
            long lValue = Convert.ToInt64(value);
            long lFlag = Convert.ToInt64(flags);
            if (on)
            {
                lValue |= lFlag;
            }
            else
            {
                lValue &= (~lFlag);
            }
            return (T)Enum.ToObject(typeof(T), lValue);
        }
    
        public static T SetFlags<T>(this T value, T flags) where T : struct
        {
            return value.SetFlags(flags, true);
        }
    
        public static T ClearFlags<T>(this T value, T flags) where T : struct
        {
            return value.SetFlags(flags, false);
        }
    
        public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct
        {
            CheckEnumWithFlags<T>();
            long lValue = 0;
            foreach (T flag in flags)
            {
                long lFlag = Convert.ToInt64(flag);
                lValue |= lFlag;
            }
            return (T)Enum.ToObject(typeof(T), lValue);
        }
    }
    

    The main drawback is that you can't specify where T : Enum : it is explicitly forbidden ("Constraint cannot be special class 'System.Enum'"), so the extension methods will appear in intellisense for all structs... I added the CheckEnumWithFlags method to check that the type is actually an enum, and has the Flags attribute.


    UPDATE : Jon Skeet recently started an interesting library called UnconstrainedMelody which does exactly the same sort of things, and works around the generic type constraint limitation mentioned above

    0 讨论(0)
  • 2021-02-07 19:16

    Simply use the Enum.HasFlag() Method !

    0 讨论(0)
  • 2021-02-07 19:18

    Personally, I think that look fine because you've wrapped it into a single purpose function. If you had that code scattered through an entire program I think you would have some problems, but what you've created improves clarity everywhere it is used and the function itself is clear enough what it does.

    Just my opinion of course.

    You could though, use the is keyword, which might help a little

    public static bool IsSet<T>(this T value, T flags) where T : Enum
    { 
        if (value is int)
        {
            return ((int)(object)a & (int)(object)b) == (int)(object)b);
        }
        //etc...
    
    0 讨论(0)
  • 2021-02-07 19:18

    or... public static bool IsSet(this Enum value, Enum compare) { int baseValue = value.ToInt32(); int compareValue = compare.ToInt32(); if (baseValue == 0) return false; return ((baseValue & compareValue) == compareValue); }

    0 讨论(0)
提交回复
热议问题