You can use
System.Linq.Expressions
to avoid converting the
Enum
value to an integer type.
Here's a solution inspired by
the MiscUtil project[
^]:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
public static class EnumExtensions
{
private static class GenericOperator<T>
{
public static readonly Func<T, T> Not = Operator(Expression.Not);
public static readonly Func<T, T, T> Or = Operator<T>(Expression.Or);
public static readonly Func<T, T, T> And = Operator<T>(Expression.And);
public static readonly Func<T, T, bool> Equal = Operator<bool>(Expression.Equal);
private static Func<T, T> Operator(Func<Expression, UnaryExpression> body)
{
try
{
Type typeT = typeof(T);
var param = Expression.Parameter(typeT, "p");
if (typeT.IsEnum)
{
Type enumType = Enum.GetUnderlyingType(typeT);
var x = Expression.Convert(param, enumType);
var op = Expression.Convert(body(x), typeT);
return Expression.Lambda<Func<T, T>>(op, param).Compile();
}
return Expression.Lambda<Func<T, T>>(body(param), param).Compile();
}
catch (InvalidOperationException ex)
{
string message = ex.Message;
return delegate { throw new InvalidOperationException(message); };
}
catch (ArgumentException ex)
{
string message = ex.Message;
return delegate { throw new InvalidOperationException(message); };
}
}
private static Func<T, T, TResult> Operator<TResult>(Func<Expression, Expression, BinaryExpression> body)
{
try
{
Type typeT = typeof(T);
var left = Expression.Parameter(typeT, "left");
var right = Expression.Parameter(typeT, "right");
if (typeT.IsEnum)
{
Type enumType = Enum.GetUnderlyingType(typeT);
var x = Expression.Convert(left, enumType);
var y = Expression.Convert(right, enumType);
Expression op = body(x, y);
if (op.Type == enumType) op = Expression.Convert(op, typeT);
return Expression.Lambda<Func<T, T, TResult>>(op, left, right).Compile();
}
return Expression.Lambda<Func<T, T, TResult>>(body(left, right), left, right).Compile();
}
catch (InvalidOperationException ex)
{
string message = ex.Message;
return delegate { throw new InvalidOperationException(message); };
}
catch (ArgumentException ex)
{
string message = ex.Message;
return delegate { throw new InvalidOperationException(message); };
}
}
}
private sealed class Cache<T> where T : struct, IFormattable, IComparable
{
public static readonly bool IsFlags = Attribute.IsDefined(typeof(T), typeof(FlagsAttribute));
public static readonly T[] Values = (T[])Enum.GetValues(typeof(T));
private static readonly T _valuesCombined;
private static readonly T _invalidMask;
static Cache()
{
_valuesCombined = Values.Aggregate(GenericOperator<T>.Or);
_invalidMask = GenericOperator<T>.Not(_valuesCombined);
}
public static bool IsValid(T value)
{
bool result;
if (GenericOperator<T>.Equal(default(T), value))
{
result = true;
}
else if (IsFlags)
{
T masked = GenericOperator<T>.And(value, _invalidMask);
result = GenericOperator<T>.Equal(default(T), masked);
}
else
{
result = -1 != Array.IndexOf(Values, value);
}
return result;
}
}
public static IEnumerable<T> GetValues<T>() where T : struct, IComparable, IFormattable
{
var values = Cache<T>.Values;
if (null == values || 0 == values.Length) return Enumerable.Empty<T>();
return values.Select(x => x);
}
public static bool IsDefined<T>(T value) where T : struct, IComparable, IFormattable
{
return Cache<T>.IsValid(value);
}
public static bool IsFlags<T>() where T : struct, IComparable, IFormattable
{
return Cache<T>.IsFlags;
}
}