Checking Flags in C# Enums
Update 2021/09/22: The Enum class has a built-in HasFlag() method now.
I like C# enums and I also like using them as bitfields, even though apparently not everyone does. I realize they aren’t perfectly typesafe, but then I don’t think that’s the problem Abrams and company were trying to solve anyway.
Here’s an example one:
[Flags] public enum Fruits { Apple = 1, Banana = 2, Cherry = 4, Date = 8, Eggplant = 16 }
Nice, clean syntax. The way they solved C++’s name collision issues with enum
values is genius: Fruits.Apple. Clearly these guys are using the old noggin.
The annoying bit (argh, a pun)#the-annoying-bit-argh-a-pun
The one thing that does annoy me about flag enums is the syntax to see if a given flag (or set of flags) is set:
if ((myFruit & Fruits.Date) == Fruits.Date)
I’m not afraid of bitwise operators, but there’s some serious lameness in
here. Needing to specify the explicit == for type safety and having to use
the parenthesis because the operator precedence puts == before & first?
Gross.
For every nail there is a hammer#for-every-nail-there-is-a-hammer
Behold the solution:
public static class FruitsExtensions { public static bool IsSet(this Fruits fruits, Fruits flags) { return (fruits & flags) == flags; } }
With that, you can just do:
if (myFruit.IsSet(Fruits.Date))
Much nicer. For kicks, here’s some other useful methods:
public static class FruitsExtensions { public static bool IsSet(this Fruits fruits, Fruits flags) { return (fruits & flags) == flags; } public static bool IsNotSet(this Fruits fruits, Fruits flags) { return (fruits & (~flags)) == 0; } public static Fruits Set(this Fruits fruits, Fruits flags) { return fruits | flags; } public static Fruits Clear(this Fruits fruits, Fruits flags) { return fruits & (~flags); } }
Useful, no?
Why solve one when you can solve n?#why-solve-one-when-you-can-solve-n
So, if you’re like me and this guy, right now you’re thinking, “This just fixes one enum. Can I solve it for all enums?” Ideally, you’d do something like:
public static class EnumExtensions { public static bool IsSet<T>(this T value, T flags) where T : Enum { return (value & flags) == flags; } }
Unfortunately, that doesn’t fly. You can’t use Enum as a constraint.
Likewise, there’s no way to require a typeparam to implement an operator (like
& above). You can implement a generic solution for this:
public static class EnumExtensions { public static bool IsSet<T>(this T value, T flags) where T : struct { Type type = typeof(T); // only works with enums if (!type.IsEnum) throw new ArgumentException( "The type parameter T must be an enum type."); // handle each underlying type Type numberType = Enum.GetUnderlyingType(type); if (numberType.Equals(typeof(int))) { return Box<int>(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(sbyte))) { return Box<sbyte>(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(byte))) { return Box<byte>(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(short))) { return Box<short>(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(ushort))) { return Box<ushort>(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(uint))) { return Box<uint>(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(long))) { return Box<long>(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(ulong))) { return Box<ulong>(value, flags, (a, b) => (a & b) == b); } else if (numberType.Equals(typeof(char))) { return Box<char>(value, flags, (a, b) => (a & b) == b); } else { throw new ArgumentException( "Unknown enum underlying type " + numberType.Name + "."); } } /// Helper function for handling the value types. Boxes the /// params to object so that the cast can be called on them. private static bool Box<T>(object value, object flags, Func<T, T, bool> op) { return op((T)value, (T)flags); } }
…but, yeah, it’s not exactly fun using reflection for this.