Extension methods for common enum operations

I found myself frequently writing similar code for flags enums.  Following the DRY principle I thought extension methods would be the way to go, but came up against a few obstacles.

I found this post on stackoverflow but thought some of the solutions were a little messy and that C# 4 provided some more elegant options.  Jon Skeet provides an alternative approach here using Post Sharp.  But I wanted to keep things in C# for the time being.  So here is my solution (with comments removed).  It's not super robust, but I didn't want to increase the overhead with 'belts and braces' validation.

    public static class EnumExtensions
    {
        public static T AddFlag(this T enumObject, T value) where T : struct
        {
            if (!Enum.IsDefined(enumObject.GetType(), value)) throw new ArgumentException(value.ToString() + " cannot be added to type " + enumObject.GetType().ToString());
            dynamic temp = enumObject;
            return temp |= value;
        }

        public static T RemoveFlag(this T enumObject, T value) where T : struct
        {
            if (!Enum.IsDefined(enumObject.GetType(), value)) throw new ArgumentException(value.ToString() + " cannot be removed from type " + enumObject.GetType().ToString());
            dynamic temp = enumObject;
            dynamic tempValue = value;
            return temp &= ~tempValue;
        }

        public static T ToggleFlag(this T enumObject, T value) where T : struct
        {
            if (!Enum.IsDefined(enumObject.GetType(), value)) throw new ArgumentException(value.ToString() + " cannot be changed on type " + enumObject.GetType().ToString());
            dynamic temp = enumObject;
            dynamic tempValue = value;
            return temp ^= tempValue;
        }

        public static bool HasFlag(this T enumObject, T value) where T : struct
        {
            if (!Enum.IsDefined(enumObject.GetType(), value)) throw new ArgumentException(value.ToString() + " cannot be compared on type " + enumObject.GetType().ToString());
            dynamic temp = enumObject;
            dynamic tempValue = value;
            return (temp & tempValue) == tempValue;
        }

        public static bool Is(this T enumObject, T value) where T : struct
        {
            if (!Enum.IsDefined(enumObject.GetType(), value)) throw new ArgumentException(value.ToString() + " cannot be compared with type " + enumObject.GetType().ToString());
            dynamic temp = enumObject;
            dynamic tempValue = value;
            return temp == tempValue;
        }
    }

Points to note:

  • You cannot constrain the extension method to System.Enum so I have reduced the constraint to struct and introduced a further type check.
  • Because you can't constrain to System.Enum, attempting to cast to an enum for bitwise operations fails. Using C# 4's dynamic avoids compile time errors while permitting the operation to proceed successfully at runtime.
  • Because the methods are static, when the Add, Remove, and Toggle methods are used, you need to assign the result to a new instance.
  • The Enum.IsDefined method throws an Argument Exception if the type is not an enum.  See the MSDN documentation here.

Example usage:

    [Flags]
    public enum SampleEnum
    {
        None = 0,
        First,
        Second,
        All = First | Second
    }


    public void Example1()
    {
        SampleEnum test = SampleEnum.None;
        SampleEnum test2 = test.AddFlag(SampleEnum.First);
        bool assigned = test2.HasFlag(SampleEnum.First);
    }

    public void Example2()
    {
        SampleEnum test = SampleEnum.None;
        test = test.AddFlag(SampleEnum.First);
        bool assigned = test.HasFlag(SampleEnum.First);
    }

Comments