Loop through flag enum while ignoring combined types

Viewed 58

I am trying to have a method which fills a list based on the flags set in the enum. My problem is that the iteration through the enum iterates over all elements and not only the elements with one bit set:

using System;
using System.Collections.Generic;
                    
public class Program
{   
    [Flags]
    public enum Modes
    {
        A   = 1 << 0,
        B   = 1 << 1,
        C   = 1 << 2,
        AC  = A | C,
        All = ~0
    }
    
    public class Type
    {
        public Type(Modes mode){}
    }
    
    public static List<Type> Create(Modes modesToCreate = Modes.All)
    {
        var list = new List<Type>();
        foreach(Modes mode in Enum.GetValues(typeof(Modes)))
        {
            if(modesToCreate.HasFlag(mode))
            {
                Console.WriteLine(mode);    
                list.Add(new Type(mode));
            }
        }
        Console.WriteLine();
        return list;
    }
    
    public static void Main()
    {
        Create();
        Create(Modes.A | Modes.C);
    }
}

Real output:

A
B
C
AC
All

A
C
AC

Desired Output:

A
B
C

A
C

How can I ignore the combined flags on the iteration or only iterate over the single bit enum values?

1 Answers

You could use BitOperations.PopCount() to check if the mode has more than one bit set:

public static List<Type> Create(Modes modesToCreate = Modes.All)
{
    var list = new List<Type>();

    foreach (Modes mode in Enum.GetValues(typeof(Modes)))
    {
        if (modesToCreate.HasFlag(mode) && BitOperations.PopCount((uint)mode) == 1)
        {
            Console.WriteLine(mode);
            list.Add(new Type(mode));
        }
    }

    Console.WriteLine();
    return list;
}

If using .net Framework 4.8 or earlier, you can't use PopCount. In that case, you can count the bits using one of the answers to this question:

How to count the number of set bits in a 32-bit integer?

Alternatively (and probably better) you can just use the well-known bit-twiddle to determine if an integer is a power of two:

(n & (n - 1)) == 0

In which case the code would be:

if (modesToCreate.HasFlag(mode) && (mode & (mode - 1)) == 0)

Although for clarity I'd write that as:

bool isPowerOfTwo = (mode & (mode - 1)) == 0;

if (isPowerOfTwo && modesToCreate.HasFlag(mode))

And as PanagiotisKanavos points out in a comment below, in .net 6.0+ you can use BitOperations.IsPow2():

if (modesToCreate.HasFlag(mode) && BitOperations.IsPow2((uint)mode))
Related