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.
Points to note:
Example usage:
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;
}
}
{
public static T AddFlag
{
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
{
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
{
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
{
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
{
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);
}
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
Post a Comment