Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

See if a Flags enum is valid

0.00/5 (No votes)
17 May 2011CPOL 5.8K  
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...
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[^]:

C#
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;
   }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)