2

I have enum flags. How to get all combinations of flags that do not contain a specific one?

[Flags]
public enum TestEnum
{
    A = 1,
    B = 2,
    C = 4,
    D = 8
}

public static IEnumerable<Enum> AllNotContaining(this Enum value)
{....}

For example, TestEnum.A.AllNotContaining() should return {2, 4, 6, 8, 10, 12, 14}.

CompuChip
  • 9,143
  • 4
  • 24
  • 48
panmigal
  • 101
  • 2
  • 7
  • 1
    So if `value = TestEnum.A | TestEnum.C` you would like your function to return `{ TestEnum.B, TestEnum.D }`? – CompuChip Sep 20 '18 at 07:49
  • Example TestEnum.A.AllNotContaining() return {2, 4 ,8, 6, 10, 12, 14} – panmigal Sep 20 '18 at 07:52
  • Add your example to the question. Comments don't count here. – H H Sep 20 '18 at 07:53
  • @panmigal Please note that flagged Enums should only have values of 2^x = {1,2,4,8,16,32,...}. Otherwise this will result in errors. – Chrᴉz remembers Monica Sep 20 '18 at 08:04
  • 1
    @Chrᴉz: I think you will find that the Flags attribute does not cause any errors when an enum value is not a power of two. See: https://stackoverflow.com/questions/8447/what-does-the-flags-enum-attribute-mean-in-c – PaulF Sep 20 '18 at 08:16
  • @PaulF that's why I wrote "should". I have values like 2=ValA, 3=ValB and 4=ValC. Now checking ValB.HasFlag(ValA) returns true[Pseudocode]. – Chrᴉz remembers Monica Sep 20 '18 at 08:35
  • @Chrᴉz: but that is not an error as far as C# is concerned - if it is an error for your application - then that is a design error in your code. In other applications ValB.HasFlag(ValA) returning true maybe perfectly valid. – PaulF Sep 20 '18 at 09:13

3 Answers3

4

Step 1, use the binary NOT:

var notUsedBits = ~ value;

But this will set all of the 32 bits that were not used.

So you will probably want a mask:

[Flags]
public enum TestEnum
{
    A = 1,
    B = 2,
    C = 4,
    D = 8,    
    All = A|B|C|D,  // or compute this inside the method    
}

and then the method becomes

// untested
public static TestEnum AllNotContaining(this TestEnum value)
{
    return ~ value & TestEnum.All;
}

this does not return an IEnumerable but that is weird (and inefficient) for a Flags enum anyway.

H H
  • 263,252
  • 30
  • 330
  • 514
1

I haven't tried to polish the code below, but you should get the general idea:

  public static IEnumerable<int> AllNotContaining<T>(this T value)
    // where T : Enum  (as of C# 7.3).
  {
    // Determine upper bound of values to check.
    // E.g. for your test enum, the maximum value is 8 so we need to check up to 15.
    var values = Enum.GetValues(typeof(T)).Cast<int>();
    int max = values.Max() * 2 - 1;

    // Test all values to see if the given flag is present. If not, return it.
    for(int i = 0; i <= max; ++i)
    {
      // Possibly also: if( ((Enum)i).HasFlags(value))
      if((max & Convert.ToInt32(value)) == 0)
      {
        yield return i;
      }
    }
  }
CompuChip
  • 9,143
  • 4
  • 24
  • 48
0

Try like this:

public static IEnumerable<TestEnum> AllNotContaining(this TestEnum value)
{
    return Enum.GetValues(typeof(TestEnum)).Cast<TestEnum>().Where(x => x != value).AsEnumerable();
}
Hossein
  • 3,083
  • 3
  • 16
  • 33