Skip to main content

C# extension methods for combining and using Enum type flags.

using System;

namespace Extensions
{
    /// <summary>
    /// Extension methods to make working with Enum values easier
    /// </summary>
    public static class EnumExtensions
    {
        /// <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 != null)
            {
                result = Convert.ToInt64(value) | (long)parsed.Signed;
            }

            // return the final value
            return (T)Enum.Parse(type, result.ToString());
        }

        /// <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 != null)
            {
                result = Convert.ToInt64(value) & ~(long)parsed.Signed;
            }

            // return the final value
            return (T)Enum.Parse(type, result.ToString());
        }

        /// <summary>
        /// Checks if an enumerated type contains a value
        /// </summary>
        public static bool Has<T>(this Enum value, T check)
        {
            Type type = value.GetType();

            //determine the values
            var parsed = new Value(check, type);
            return parsed.Signed != null &&
                (Convert.ToInt64(value) & (long)parsed.Signed) == (long)parsed.Signed;
        }

        /// <summary>
        /// Checks if an enumerated type is missing a value
        /// </summary>
        public static bool Missing<T>(this Enum obj, T value)
        {
            return !Has(obj, value);
        }

        /// <summary>
        /// Internal class to simplfy narrowing values between a 
        /// ulong and long since either value should cover any 
        /// lesser value.
        /// </summary>
        private class Value
        {
            public readonly long? Signed;

            // cached comparisons for tye to use
            private static readonly Type _uInt64 = typeof(ulong);
            private static readonly Type _uInt32 = typeof(long);

            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 == _uInt32 || compare == _uInt64)
                {
                    Unsigned = Convert.ToUInt64(value);
                }
                //otherwise, a long should cover anything else
                else
                {
                    Signed = Convert.ToInt64(value);
                }
            }

            public ulong? Unsigned { get; set; }
        }
    }
}

// ----------------------------------------
// Example Usage
// ----------------------------------------

[Flags]
internal enum PermissionTypes
{
    None = 0,
    Read = 1,
    Write = 2,
    Modify = 4,
    Delete = 8,
    Create = 16,
    All = Read | Write | Modify | Delete | Create
}

// start with a value
PermissionTypes permissions = PermissionTypes.Read | PermissionTypes.Write;

//then check for the values
bool canRead = permissions.Has(PermissionTypes.Read); // true
bool canWrite = permissions.Has(PermissionTypes.Write); // true
bool canDelete = permissions.Has(PermissionTypes.Delete); // false


// 
// http://hugoware.net/blog/enumeration-extensions-2-0
// 

//create the typical object
RegexOptions options = RegexOptions.None;

//Assign a value
options = options.Include(RegexOptions.IgnoreCase); 
//options = IgnoreCase

//Or assign multiple values
options = options.Include(RegexOptions.Multiline | RegexOptions.Singleline); 
//options = IgnoreCase, Multiline, Singleline

//Remove values from the list
options = options.Remove(RegexOptions.IgnoreCase); 
//options = Multiline, Singleline

//Check if a value even exists
bool multiline = options.Has(RegexOptions.Multiline); //true
bool ignoreCase = options.Missing(RegexOptions.IgnoreCase); //true