8
[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

SwDevMan81
  • 48,814
  • 22
  • 151
  • 184
EKS
  • 5,543
  • 6
  • 44
  • 60

8 Answers8

7

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.

Guvante
  • 18,775
  • 1
  • 33
  • 64
  • Sorry for being a amature here, but how do you call this function? – EKS May 25 '11 at 14:25
  • The enum type is passed as a type parameter, in your example: `GetAllEnums()` note that you would need to place it in a class, and likely preface it with that class name, for instance if it were in a static class called Utility, then `Utility.GetAllEnums()`. – Guvante May 25 '11 at 17:52
  • New in C# 7.3: `where T : System.Enum` https://github.com/dotnet/docs/issues/3964 – BurnsBA Dec 06 '18 at 15:33
5

Since it's a flagged enum, why not simply:

  1. Get the highest vale of the enum.
  2. Calculate the amound of combinations, i.e. the upper bound.
  3. Iterate over every combination, i.e. loop from 0 to upper bound.

An example would look like this

var highestEnum = Enum.GetValues(typeof(MyEnum)).Cast<int>().Max();
var upperBound = highestEnum * 2;    
for (int i = 0; i < upperBound; i++)
{
    Console.WriteLine(((MyEnum)i).ToString());
}
M. Mimpen
  • 1,212
  • 16
  • 21
2
    public IEnumerable<TEnum> AllCombinations<TEnum>() where TEnum : struct
    {
        Type enumType = typeof (TEnum);
        if (!enumType.IsEnum)
            throw new ArgumentException(string.Format("The type {0} does not represent an enumeration.", enumType), "TEnum");

        if (enumType.GetCustomAttributes(typeof (FlagsAttribute), true).Length > 0) //Has Flags attribute
        {
            var allCombinations = new HashSet<TEnum>();

            var underlyingType = Enum.GetUnderlyingType(enumType);
            if (underlyingType == typeof (sbyte) || underlyingType == typeof (short) || underlyingType == typeof (int) || underlyingType == typeof (long))
            {
                long[] enumValues = Array.ConvertAll((TEnum[]) Enum.GetValues(enumType), value => Convert.ToInt64(value));
                for (int i = 0; i < enumValues.Length; i++)
                    FillCombinationsRecursive(enumValues[i], i + 1, enumValues, allCombinations);
            }
            else if (underlyingType == typeof (byte) || underlyingType == typeof (ushort) || underlyingType == typeof (uint) || underlyingType == typeof (ulong))
            {
                ulong[] enumValues = Array.ConvertAll((TEnum[]) Enum.GetValues(enumType), value => Convert.ToUInt64(value));
                for (int i = 0; i < enumValues.Length; i++)
                    FillCombinationsRecursive(enumValues[i], i + 1, enumValues, allCombinations);
            }
            return allCombinations;
        }
        //No Flags attribute
        return (TEnum[]) Enum.GetValues(enumType);
    }

    private void FillCombinationsRecursive<TEnum>(long combination, int start, long[] initialValues, HashSet<TEnum> combinations) where TEnum : struct
    {
        combinations.Add((TEnum)Enum.ToObject(typeof(TEnum), combination));
        if (combination == 0)
            return;

        for (int i = start; i < initialValues.Length; i++)
        {
            var nextCombination = combination | initialValues[i];
            FillCombinationsRecursive(nextCombination, i + 1, initialValues, combinations);
        }
    }

    private void FillCombinationsRecursive<TEnum>(ulong combination, int start, ulong[] initialValues, HashSet<TEnum> combinations) where TEnum : struct
    {
        combinations.Add((TEnum)Enum.ToObject(typeof(TEnum), combination));
        if (combination == 0)
            return;

        for (int i = start; i < initialValues.Length; i++)
        {
            var nextCombination = combination | initialValues[i];
            FillCombinationsRecursive(nextCombination, i + 1, initialValues, combinations);
        }
    }
Aleksei
  • 51
  • 1
  • 2
    Please add some text to explain the code. Does it have to be that long? – krlmlr Oct 24 '12 at 20:56
  • Well, in the first methode AllCombinations, first of all I check if the type is Enum. Then I want to be shure that it has Flags attribute.If so, I analyze underlying type, which can be any integral type. If it is one of signed integers, I cast to the largest signed one (long) in order to not lose data. If it is one of unsigned integers, I cast to the largest unsigned one (ulong). After that I just call recursive function which does the job. The second and the third methods are almost identical except the types of input parameters. I could not use generic type because of bitwise OR operator. – Aleksei Oct 25 '12 at 23:11
2

Here is a solution particular to your code sample, using a simple for loop (don't use, see update below)

int max = (int)(MyEnum.Setting1 | MyEnum.Setting2 | MyEnum.Setting3 | MyEnum.Setting4);
for (int i = 0; i <= max; i++)
{
    var value = (MyEnum)i;
    SomeOtherFunction(value);
}

Update: Here is a generic method that will return all possible combinations. And thank @David Yaw for the idea to use a queue to build up every combination.

IEnumerable<T> AllCombinations<T>() where T : struct
{
    // Constuct a function for OR-ing together two enums
    Type type = typeof(T);
    var param1 = Expression.Parameter(type);
    var param2 = Expression.Parameter(type);
    var orFunction = Expression.Lambda<Func<T, T, T>>(
        Expression.Convert(
            Expression.Or(
                Expression.Convert(param1, type.GetEnumUnderlyingType()),
                Expression.Convert(param2, type.GetEnumUnderlyingType())),
            type), param1, param2).Compile();

    var initalValues = (T[])Enum.GetValues(type);
    var discoveredCombinations = new HashSet<T>(initalValues);
    var queue = new Queue<T>(initalValues);

    // Try OR-ing every inital value to each value in the queue
    while (queue.Count > 0)
    {
        T a = queue.Dequeue();
        foreach (T b in initalValues)
        {
            T combo = orFunction(a, b);
            if (discoveredCombinations.Add(combo))
                queue.Enqueue(combo);
        }
    }

    return discoveredCombinations;
}
Community
  • 1
  • 1
Greg
  • 23,155
  • 11
  • 57
  • 79
  • This works in the given case, but will not work if there are some powers of 2 that are not members smaller then the largest member. – Ian Ringrose May 24 '11 at 21:29
  • MyEnum.Setting4 wouldn't be high enough, you would need setting1 &2&3&4 would you not? – dnolan May 24 '11 at 21:29
  • @dnolan - Thank you for pointing that out, how dense of me. I have fixed my answer. – Greg May 24 '11 at 21:31
  • @Ian - No argument there. A generalizable solution would be more complex. – Greg May 24 '11 at 21:32
  • @Greg no problem, as @Ian points out, this solution would work if the assumption is made that all flags are used within that range, if one is missing it would break down. – dnolan May 24 '11 at 21:33
  • I have an Enum utility class that could probably solve this problem without too much trouble. I'll dig it out when I get home. – Greg May 24 '11 at 21:35
1

First, grab a list of all the individual values. Since you've got 5 values, that's (1 << 5) = 32 combinations, so iterate from 1 to 31. (Don't start at zero, that would mean to include none of the enum values.) When iterating, examine the bits in the number, every one bit in the iteration variable means to include that enum value. Put the results into a HashSet, so that there aren't duplicates, since including the 'None' value doesn't change the resulting enum.

List<MyEnum> allValues = new List<MyEnum>(Enum.Getvalues(typeof(MyEnum)));
HashSet<MyEnum> allCombos = new Hashset<MyEnum>();

for(int i = 1; i < (1<<allValues.Count); i++)
{
    MyEnum working = (MyEnum)0;
    int index = 0;
    int checker = i;
    while(checker != 0)
    {
        if(checker & 0x01 == 0x01) working |= allValues[index];
        checker = checker >> 1;
        index++;
    }
    allCombos.Add(working);
}
David Yaw
  • 27,383
  • 4
  • 60
  • 93
0

I'm probably a little late to the party I would like to leave my solution which also includes the values plus the possible text of the combination in form of ("V1 | V2", "V1 | V2 | V3", etc).

I took some aspects of the solutions proposed above, so thanks to all that had posted the previous answers :D.

Note: only work with Enum set as base 2 combinations.

public static Dictionary<int,string> GetCombinations( this Enum enu)
    {
        var fields = enu.GetType()
                        .GetFields()
                        .Where(f => f.Name != "value__")
                        .DistinctBy(f=> Convert.ToInt32(f.GetRawConstantValue()));

        var result = fields.ToDictionary(f=>Convert.ToInt32(f.GetRawConstantValue()), f => f.Name);

        int max = Enum.GetValues(enu.GetType()).Cast<int>().Max();
        int upperBound = max * 2;

        for (int i = 0 ; i <= upperBound ; i += 2)
        {
            string s = Convert.ToString(i, 2).PadLeft(Math.Abs(i-max),'0');
            Boolean[] bits = s.Select(chs => chs == '1' ? true : false)
                             .Reverse()
                             .ToArray();

            if (!result.ContainsKey(i))
            {
                var newComb = string.Empty;
                for (int j = 1; j < bits.Count(); j++)
                {
                    var idx = 1 << j;
                    if (bits[j] && result.ContainsKey(idx))
                    {
                        newComb = newComb + result[idx] + " | ";
                    }
                }                                      
                newComb = newComb.Trim(new char[] { ' ', '|' });
                if (!result.ContainsValue(newComb) && !string.IsNullOrEmpty(newComb))
                {
                    result.Add(i, newComb);
                }                    
            }
        }
        return result;
    }
Enrique Ramos
  • 36
  • 1
  • 5
  • And what is the value that it adds, by just reformatting existing answers? – sim Aug 06 '18 at 20:38
  • Well previous answers return only the number as result of the combination, mine is looking to return also the Display Values that form the combination. – Enrique Ramos Aug 06 '18 at 22:22
0

Concluded with this version:

Without checks:

public IEnumerable<T> AllCombinations<T>() where T : struct
{
    var type = typeof(T);
    for (var combination = 0; combination < Enum.GetValues(type).Cast<int>().Max()*2; combination++)
    {
        yield return (T)Enum.ToObject(type, combination);
    }
}

With some checks:

public IEnumerable<T> AllCombinations<T>() where T : struct
{
    var type = typeof(T);
    if (!type.IsEnum)
    {
        throw new ArgumentException($"Type parameter '{nameof(T)}' must be an Enum type.");
    }

    for (var combination = 0; combination < Enum.GetValues(type).Cast<int>().Max()*2; combination++)
    {
        var result = (T)Enum.ToObject(type, combination);

        // Optional check for legal combination.
        // (and is not necessary if all flag a ascending exponent of 2 like 2, 4, 8...
        if (result.ToString() == combination.ToString() && combination != 0)
        {
            continue;
        }

        yield return result;
    }
}
g.pickardou
  • 32,346
  • 36
  • 123
  • 268
0

I usually don't want to update each variable representing the max of an enumeration, when I add a new member to an enumeration. For example I dislike the statement Greg proposed:

int max = (int)(MyEnum.Setting1 | MyEnum.Setting2 | ... | MyEnum.SettingN);

Consider when you have several of these variabeles scattered throughout your solution and you decide to modify your enumeration. That surely isn't a desirable scenario.

I will admit in advance that my code is slower, but it is automatically correct after an enumeration has been modified, and I strive to code in such a robust manner. I'm willing to pay some computational penalty for that, C# is all about that anywayz. I propose:

public static IEnumerable<T> GetAllValues<T>() where T : struct
{
    if (!typeof(T).IsEnum) throw new ArgumentException("Generic argument is not an enumeration type");
    int maxEnumValue = (1 << Enum.GetValues(typeof(T)).Length) - 1;
    return Enumerable.Range(0, maxEnumValue).Cast<T>();
}

This assumes the enumeration contains members for all powers of 2 up to a certain power(including 0), exactly like a flag-enumeration is usually used.

JBSnorro
  • 6,048
  • 3
  • 41
  • 62