Generic extension method to see if an enum contains a flag

前端 未结 8 1101
闹比i
闹比i 2021-02-01 01:56

Considering this:

[Flags]
public enum MyEnum {
    One = 1,
    Two = 2,
    Four = 4,
    Eight = 8
}

public static class FlagsHelper
{
    public static bool          


        
8条回答
  •  清歌不尽
    2021-02-01 02:22

    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 
      /// 
      /// 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. 
      /// 
      /// The type of the enum.
      /// The value.
      /// The flag.
      /// 
      ///  true if the specified value has flags; otherwise, false.
      /// 
      /// If TEnum is not an enum.
      public static bool HasFlags(this TEnum value, TEnum flag) where TEnum:struct,IComparable,IConvertible,IFormattable
      {
       return EnumExtensionsInternal.HasFlagsDelegate(value, flag);
      }
      #endregion Public Static Methods 
    
      #region Nested Classes 
    
      static class EnumExtensionsInternal where TEnum : struct,IComparable, IConvertible, IFormattable
      {
      #region Public Static Variables 
       /// 
       /// The delegate which determines if a flag is set.
       /// 
       public static readonly Func HasFlagsDelegate = CreateHasFlagDelegate();
      #endregion Public Static Variables 
    
      #region Private Static Methods 
       /// 
       /// Creates the has flag delegate.
       /// 
       /// 
       private static Func 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> lambdaExpression = Expression.Lambda>(
          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 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> lambdaExpression = Expression.Lambda>(
                                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.

提交回复
热议问题