Search code examples
c#enum-flags

How do I get all possible combinations of an enum ( Flags )


[Flags]
public enum MyEnum
{
    None = 0,
    Setting1 = (1 << 1),
    Setting2 = (1 << 2),
    Setting3 = (1 << 3),
    Setting4 = (1 << 4),
}

I need to be able to somehow loop over every posible setting and pass the settings combination to a function. Sadly I have been unable to figure out how to do this


Solution

  • Not tested, use at your own risk, but should solve the problem generically enough. System.Enum is not a valid restriction as technically C# only allow inheritance in/with class the backend bypasses this for Enum and ValueType. So sorry for the ugly casting. It is also not horribly efficient but unless you are running this against a dynamically generated type it should only ever have to be done once per execution (or once period if saved).

    public static List<T> GetAllEnums<T>()
        where T : struct
        // With C# 7.3 where T : Enum works
    {
        // Unneeded if you add T : Enum
        if (typeof(T).BaseType != typeof(Enum)) throw new ArgumentException("T must be an Enum type");
    
        // The return type of Enum.GetValues is Array but it is effectively int[] per docs
        // This bit converts to int[]
        var values = Enum.GetValues(typeof(T)).Cast<int>().ToArray();
    
        if (!typeof(T).GetCustomAttributes(typeof(FlagsAttribute), false).Any())
        {
            // We don't have flags so just return the result of GetValues
            return values;
        }
    
        var valuesInverted = values.Select(v => ~v).ToArray();
        int max = 0;
        for (int i = 0; i < values.Length; i++)
        {
            max |= values[i];
        }
    
        var result = new List<T>();
        for (int i = 0; i <= max; i++)
        {
            int unaccountedBits = i;
            for (int j = 0; j < valuesInverted.Length; j++)
            {
                // This step removes each flag that is set in one of the Enums thus ensuring that an Enum with missing bits won't be passed an int that has those bits set
                unaccountedBits &= valuesInverted[j];
                if (unaccountedBits == 0)
                {
                    result.Add((T)(object)i);
                    break;
                }
            }
        }
    
        //Check for zero
        try
        {
            if (string.IsNullOrEmpty(Enum.GetName(typeof(T), (T)(object)0)))
            {
                result.Remove((T)(object)0);
            }
        }
        catch
        {
            result.Remove((T)(object)0);
        }
    
        return result;
    }
    

    This works by getting all the values and ORing them together, rather than summing, in case there are composite numbers included. Then it takes every integer up to the maximum and masking them with the reverse of each Flag, this causes valid bits to become 0, allowing us to identify those bits that are impossible.

    The check at the end is for missing zero from an Enum. You can remove it if you are fine with always including a zero enum in the results.

    Gave the expected result of 15 when given an enum containing 2,4,6,32,34,16384.