Questo è il mio approccio è SICURO tipo e non fa alcuna boxe o di unboxing. Genera un'eccezione se il tipo non è un enum. Esiste una tecnica si può usare se si vuole trasformarlo in un metodo statico pubblico che verrà digitato a Enum di, ma non può essere un metodo di estensione poi. C'è anche bisogno di verificare la presenza di nulla, come i blocchi di struct per vincoli fuori annullabile enum di pure. Non credo ci sia molto da fare per migliorare questo codice, con l'eccezione forse di scriverlo in F # o C++/CLI in modo che si può mettere un vincolo enum su di esso. L'idea è quella di costruire una funzione tramite alberi di espressione che convertiranno l'enumerazione a uno lungo se tutt'altro che un enum ulong base, o ulong e poi e, in sostanza, la produzione :: 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
}
Come ho dimenticato che l'albero di espressione di cui sopra è NET 4 solo il metodo seguente dovrebbe funzionare in .NET 3.5 per creare lo stesso albero di espressione ::
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();
}
questa versione dovrebbe compilare in .NET 3.5 e se non lo fa io non riesco a capire perché.
Potrebbe essere di interesse: http://stackoverflow.com/questions/7244/anyone-know-a-good-work-und-for-the-lack-of-an-enum-generic-constraint –
Potrebbe essere un buona idea taggare questo con la versione di C# che stai usando, poiché l'ultima versione ha questa funzione integrata. – chilltemp
Non capisco come il tuo metodo 'Contains' aggiunga qualcosa al metodo istanza integrato' Enum.HasFlag (enum) '. Guardando il codice decompilato per quel metodo, sembra che il tuo metodo stia facendo esattamente la stessa cosa con un po 'meno di controllo degli errori. –