Este es mi enfoque se trata de un tipo seguro y no hace ningún boxeo o unboxing. Lanza una excepción si el tipo no es una enumeración. Existe una técnica que puede usar si desea convertirla en un método público estático que se escribirá en Enum, pero no puede ser un método de extensión. . Tampoco es necesario comprobar si hay nulo, ya que struct contrôt también bloquea las enum anulables. No creo que haya mucho por hacer para mejorar este código, con la excepción de tal vez escribirlo en F # o C++/CLI para que pueda poner una restricción enum sobre él. La idea es construir una función utilizando los árboles de expresión que convertirán a la enumeración sea mucho tiempo si su algo más que una enumeración basada ulong o ulong y luego y ellos, para producir esencialmente :: 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
}
Mientras olvidó que el árbol de expresión anterior es .NET 4 solamente la siguiente método debería funcionar en .NET 3.5 para crear el mismo árbol de expresión ::
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();
}
esta versión debería compilar en .NET 3.5 y si no lo hace I no puedo entender por qué
Puede ser de interés: http://stackoverflow.com/questions/7244/anyone-know-a-good-workaround-for-the-lack-of-an-enum-generic-constraint –
Podría ser un Es una buena idea etiquetar esto con la versión de C# que está utilizando, ya que la última versión tiene esta función incorporada. – chilltemp
No entiendo cómo su método 'Contiene' agrega nada sobre el método de instancia incorporado' Enum.HasFlag (Enum) '. Mirando el código descompilado para ese método, su método parece estar haciendo exactamente lo mismo con un poco menos de comprobación de errores. –