Considering this:
[Flags]
public enum MyEnum {
One = 1,
Two = 2,
Four = 4,
Eight = 8
}
public static class FlagsHelper
{
public static bool
This is my approach this is Type safe and doesn't do any boxing or unboxing. It throws an exception if the type is not an enum.
There is a technique you can use if you want to turn it into a public static method that will be typed to Enum's, but it can't be an extension method then.
There is also no need to check for null, as the struct contraint blocks out nullable enum's as well. I don't think there is much to be done to improve this code, with the exception of maybe writing it in F# or C++/CLI so that you can put an enum constraint on it.
The idea is to build a function using expression trees that will convert the enum to either long if its anything but a ulong based enum, or ulong and then and them, essentially producing::
return value & flag == flag
public static class EnumExtensions
{
#region Public Static Methods
/// <summary>
/// Determines whether the specified value has flags. Note this method is up to 60 times faster
/// than the one that comes with .NET 4 as it avoids any explict boxing or unboxing.
/// </summary>
/// <typeparam name="TEnum">The type of the enum.</typeparam>
/// <param name="value">The value.</param>
/// <param name="flag">The flag.</param>
/// <returns>
/// <c>true</c> if the specified value has flags; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentException">If TEnum is not an enum.</exception>
public static bool HasFlags<TEnum>(this TEnum value, TEnum flag) where TEnum:struct,IComparable,IConvertible,IFormattable
{
return EnumExtensionsInternal<TEnum>.HasFlagsDelegate(value, flag);
}
#endregion Public Static Methods
#region Nested Classes
static class EnumExtensionsInternal<TEnum> where TEnum : struct,IComparable, IConvertible, IFormattable
{
#region Public Static Variables
/// <summary>
/// The delegate which determines if a flag is set.
/// </summary>
public static readonly Func<TEnum, TEnum, bool> HasFlagsDelegate = CreateHasFlagDelegate();
#endregion Public Static Variables
#region Private Static Methods
/// <summary>
/// Creates the has flag delegate.
/// </summary>
/// <returns></returns>
private static Func<TEnum, TEnum, bool> CreateHasFlagDelegate()
{
if(!typeof(TEnum).IsEnum)
{
throw new ArgumentException(string.Format("{0} is not an Enum", typeof(TEnum)), typeof(EnumExtensionsInternal<>).GetGenericArguments()[0].Name);
}
ParameterExpression valueExpression = Expression.Parameter(typeof(TEnum));
ParameterExpression flagExpression = Expression.Parameter(typeof(TEnum));
ParameterExpression flagValueVariable = Expression.Variable(Type.GetTypeCode(typeof(TEnum)) == TypeCode.UInt64 ? typeof(ulong) : typeof(long));
Expression<Func<TEnum, TEnum, bool>> lambdaExpression = Expression.Lambda<Func<TEnum, TEnum, bool>>(
Expression.Block(
new[] { flagValueVariable },
Expression.Assign(
flagValueVariable,
Expression.Convert(
flagExpression,
flagValueVariable.Type
)
),
Expression.Equal(
Expression.And(
Expression.Convert(
valueExpression,
flagValueVariable.Type
),
flagValueVariable
),
flagValueVariable
)
),
valueExpression,
flagExpression
);
return lambdaExpression.Compile();
}
#endregion Private Static Methods
}
#endregion Nested Classes
}
As I forgot that the expression tree above is .NET 4 only the following method should work in .NET 3.5 to create the same expression tree::
private static Func<TEnum, TEnum, bool> CreateHasFlagDelegate2()
{
if(!typeof(TEnum).IsEnum)
{
throw new ArgumentException(string.Format("{0} is not an Enum", typeof(TEnum)), typeof(EnumExtensionsInternal<>).GetGenericArguments()[0].Name);
}
ParameterExpression valueExpression = Expression.Parameter(
typeof(TEnum),
typeof(TEnum).Name
);
ParameterExpression flagExpression = Expression.Parameter(
typeof(TEnum),
typeof(TEnum).Name
);
var targetType = Type.GetTypeCode(typeof(TEnum)) == TypeCode.UInt64 ? typeof(ulong) : typeof(long);
Expression<Func<TEnum, TEnum, bool>> lambdaExpression = Expression.Lambda<Func<TEnum, TEnum, bool>>(
Expression.Equal(
Expression.And(
Expression.Convert(
valueExpression,
targetType
),
Expression.Convert(
flagExpression,
targetType
)
),
Expression.Convert(
flagExpression,
targetType
)
),
valueExpression,
flagExpression
);
return lambdaExpression.Compile();
}
this version should compile in .NET 3.5 and if it doesn't I can't understand why.
I have another approach here that I just cooked up quickly using the fact that Delegate.CreateDelegate allows conversion between methods for Enum's and their underlying types. The following approach is much like my previous answer but I feel might be easier to read for people who don't know expression tree syntax. Basically we know that Enums only have 8 possible underlying types, and so we just create a static method for each call it could use. Since I'm going for brevity I use anonymous methods which happened to be named the same thing as the possible typecode values.This approach will work in .Net 3.5::
public static class EnumHelper
{
delegate bool HasFlag<T>(T left,T right);
static readonly HasFlag<Byte> Byte = (x,y)=> (x&y) ==y;
static readonly HasFlag<SByte> Sbyte = (x,y)=> (x&y) ==y;
static readonly HasFlag<Int16> Int16 = (x,y)=> (x&y) ==y;
static readonly HasFlag<UInt16> UInt16 = (x,y)=> (x&y) ==y;
static readonly HasFlag<Int32> Int32 = (x,y)=> (x&y) ==y;
static readonly HasFlag<UInt32> UInt32 = (x,y)=> (x&y) ==y;
static readonly HasFlag<Int64> Int64 = (x,y)=> (x&y) ==y;
static readonly HasFlag<UInt64> UInt64 = (x,y)=> (x&y) ==y;
public static bool HasFlags<TEnum>(this TEnum @enum,TEnum flag) where TEnum:struct,IConvertible,IComparable,IFormattable
{
return Enum<TEnum>.HasFlag(@enum,flag);
}
class Enum<TEnum> where TEnum:struct,IConvertible,IComparable,IFormattable
{
public static HasFlag<TEnum> HasFlag = CreateDelegate();
static HasFlag<TEnum> CreateDelegate()
{
if (!typeof(TEnum).IsEnum) throw new ArgumentException(string.Format("{0} is not an enum", typeof(TEnum)), typeof(Enum<>).GetGenericArguments()[0].Name);
var delegateName = Type.GetTypeCode(typeof(TEnum)).ToString();
var @delegate = typeof(EnumHelper).GetField(delegateName,BindingFlags.Static | BindingFlags.NonPublic).GetValue(null) as Delegate;
return Delegate.CreateDelegate(typeof(HasFlag<TEnum>), @delegate.Method) as HasFlag<TEnum>;
}
}
}
I based this method off of a bunch of SO & Google searches, and a by using reflector to see what MS did for the .NET 4 HasFlags method.
public static class EnumExt
{
/// <summary>
/// Check to see if a flags enumeration has a specific flag set.
/// </summary>
/// <param name="variable">Flags enumeration to check</param>
/// <param name="value">Flag to check for</param>
/// <returns></returns>
public static bool HasFlag(this Enum variable, Enum value)
{
if (variable == null)
return false;
if (value == null)
throw new ArgumentNullException("value");
// Not as good as the .NET 4 version of this function, but should be good enough
if (!Enum.IsDefined(variable.GetType(), value))
{
throw new ArgumentException(string.Format(
"Enumeration type mismatch. The flag is of type '{0}', was expecting '{1}'.",
value.GetType(), variable.GetType()));
}
ulong num = Convert.ToUInt64(value);
return ((Convert.ToUInt64(variable) & num) == num);
}
}
Notes:
Use caution if you define a negative number as a flag enumerated constant because many flag positions might be set to 1, which might make your code confusing and encourage coding errors.
Not sure if you're using .NET 4.0 or not, but it comes with the static method Enum.HasFlags().
-- Code Removed (the accepted solution has it already) --
You can basically use your existing extension method, byt use the Enum
type instead of MyEnum
. The problem then is that it doesn't know the enums are flags and won't allow the &
operator, so you just have to convert the enum values to numbers.
public static bool Contains(this Enum keys, Enum flag)
{
if (keys.GetType() != flag.GetType())
throw new ArgumentException("Type Mismatch");
return (Convert.ToUInt64(keys) & Convert.ToUInt64(flag)) != 0;
}
And a unit test for good measure:
[TestMethod]
public void TestContains()
{
var e1 = MyEnum.One | MyEnum.Two;
Assert.IsTrue( e1.Contains(MyEnum.Two) );
var e2 = MyEnum.One | MyEnum.Four;
Assert.IsFalse(e2.Contains(MyEnum.Two));
}
Unfortunately no there is not a good way to make an extension method like this. In order for this to work you'd need to have a generic method which operated on enum
values. Unfortunately there is no way to constrain generic arguments to be an enum
// Ilegal
public static bool Contains<T>(this T value, T flag) where T : enum {
...
}
The best I've come up with is the following
public static bool HasFlag<T>(this System.Enum e, T flag)
{
var intValue = (int)(object)e;
var intFlag = (int)(object)flag;
return (intValue & intFlag) != 0;
}
However it's limited in several ways
enum
values are int
based. e
is null