Enum.HasFlag, why no Enum.SetFlag?

前端 未结 8 2032
别跟我提以往
别跟我提以往 2020-12-24 05:38

I have to build an extension method for each flag type I declare, like so:

public static EventMessageScope SetFlag(this EventMessageScope flags, 
    EventMe         


        
相关标签:
8条回答
  • 2020-12-24 06:15

    Here is another quick and dirty way to SetFlag for any Enum:

    public static T SetFlag<T>(this T flags, T flag, bool value) where T : struct, IComparable, IFormattable, IConvertible
        {
            int flagsInt = flags.ToInt32(NumberFormatInfo.CurrentInfo);
            int flagInt = flag.ToInt32(NumberFormatInfo.CurrentInfo);
            if (value)
            {
                flagsInt |= flagInt;
            }
            else
            {
                flagsInt &= ~flagInt;
            }
            return (T)(Object)flagsInt;
        }
    
    0 讨论(0)
  • 2020-12-24 06:20
    public static class SomeEnumHelperMethodsThatMakeDoingWhatYouWantEasier
    {
        public static T IncludeAll<T>(this Enum value)
        {
            Type type = value.GetType();
            object result = value;
            string[] names = Enum.GetNames(type);
            foreach (var name in names)
            {
                ((Enum) result).Include(Enum.Parse(type, name));
            }
    
            return (T) result;
            //Enum.Parse(type, result.ToString());
        }
    
        /// <summary>
        /// Includes an enumerated type and returns the new value
        /// </summary>
        public static T Include<T>(this Enum value, T append)
        {
            Type type = value.GetType();
    
            //determine the values
            object result = value;
            var parsed = new _Value(append, type);
            if (parsed.Signed is long)
            {
                result = Convert.ToInt64(value) | (long) parsed.Signed;
            }
            else if (parsed.Unsigned is ulong)
            {
                result = Convert.ToUInt64(value) | (ulong) parsed.Unsigned;
            }
    
            //return the final value
            return (T) Enum.Parse(type, result.ToString());
        }
    
        /// <summary>
        /// Check to see if a flags enumeration has a specific flag set.
        /// </summary>
        /// <param name="variable">Flags enumeration to check</param>
        /// <param name="value">Flag to check for</param>
        /// <returns></returns>
        public static bool HasFlag(this Enum variable, Enum value)
        {
            if (variable == null)
                return false;
    
            if (value == null)
                throw new ArgumentNullException("value");
    
            // Not as good as the .NET 4 version of this function, 
            // but should be good enough
            if (!Enum.IsDefined(variable.GetType(), value))
            {
                throw new ArgumentException(string.Format(
                    "Enumeration type mismatch.  The flag is of type '{0}', " +
                    "was expecting '{1}'.", value.GetType(), 
                    variable.GetType()));
            }
    
            ulong num = Convert.ToUInt64(value);
            return ((Convert.ToUInt64(variable) & num) == num);
        }
    
    
        /// <summary>
        /// Removes an enumerated type and returns the new value
        /// </summary>
        public static T Remove<T>(this Enum value, T remove)
        {
            Type type = value.GetType();
    
            //determine the values
            object result = value;
            var parsed = new _Value(remove, type);
            if (parsed.Signed is long)
            {
                result = Convert.ToInt64(value) & ~(long) parsed.Signed;
            }
            else if (parsed.Unsigned is ulong)
            {
                result = Convert.ToUInt64(value) & ~(ulong) parsed.Unsigned;
            }
    
            //return the final value
            return (T) Enum.Parse(type, result.ToString());
        }
    
        //class to simplfy narrowing values between
        //a ulong and long since either value should
        //cover any lesser value
        private class _Value
        {
            //cached comparisons for tye to use
            private static readonly Type _UInt32 = typeof (long);
            private static readonly Type _UInt64 = typeof (ulong);
    
            public readonly long? Signed;
            public readonly ulong? Unsigned;
    
            public _Value(object value, Type type)
            {
                //make sure it is even an enum to work with
                if (!type.IsEnum)
                {
                    throw new ArgumentException(
                        "Value provided is not an enumerated type!");
                }
    
                //then check for the enumerated value
                Type compare = Enum.GetUnderlyingType(type);
    
                //if this is an unsigned long then the only
                //value that can hold it would be a ulong
                if (compare.Equals(_UInt32) || compare.Equals(_UInt64))
                {
                    Unsigned = Convert.ToUInt64(value);
                }
                    //otherwise, a long should cover anything else
                else
                {
                    Signed = Convert.ToInt64(value);
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-24 06:29

    The reason I'm finding is that since enum is a value type, you cannot pass it in and set its type. To all of you that think its stupid, I say this to you: Not all developers understand bit flags and how to turn them on or off (which is much less intuitive).

    Not a stupid idea, just not possible.

    0 讨论(0)
  • 2020-12-24 06:31

    I've done something that works for me and that's very simple...

        public static T SetFlag<T>(this Enum value, T flag, bool set)
        {
            Type underlyingType = Enum.GetUnderlyingType(value.GetType());
    
            // note: AsInt mean: math integer vs enum (not the c# int type)
            dynamic valueAsInt = Convert.ChangeType(value, underlyingType);
            dynamic flagAsInt = Convert.ChangeType(flag, underlyingType);
            if (set)
            {
                valueAsInt |= flagAsInt;
            }
            else
            {
                valueAsInt &= ~flagAsInt;
            }
    
            return (T)valueAsInt;
        }
    

    Usage:

        var fa = FileAttributes.Normal;
        fa = fa.SetFlag(FileAttributes.Hidden, true);
    
    0 讨论(0)
  • 2020-12-24 06:32

    The & operator will give you the same answer with a & b as it will with b & a, so

    (EventMessaageScope.Private).Get(EventMessageScope.Private | EventMessageScope.PublicOnly)

    is the same as writing

    (EventMessageScope.Private | EventMessageScope.PublicOnly).Get(EventMessaageScope.Private)

    If you just want to know if the value is the same as EventMessaageScope.Public, then just use equals:

    EventMessageScope.Private == EventMessageScope.Public

    Your method will always return false for (EventMessageScope.None).Get(EventMessaageScope.None) because None == 0 and it only returns true when the result of the AND operation is not zero. 0 & 0 == 0.

    0 讨论(0)
  • 2020-12-24 06:32

    To answer part of your your question: the Get function works properly according to binary logic - it checks for any match. If you want to match the whole set of flags, consider this instead:

    return ((flags & flag) != flag);
    

    Regarding "why isn't there SetFlag"... probably because it's not really needed. Flags are integers. There is already a convention for dealing with those and it applies to flags as well. If you don't want to write it with | and & - that's what the custom static addons are for - you can just use your own functions as you demonstrated yourself :)

    0 讨论(0)
提交回复
热议问题