0

Consider the following simple Flags Enum in C#:

[Flags]
public enum CountingEnum
{
    Zero = 0,
    One = 1 << 0,
    Two = 1 << 1,
    Three = Two | One,
    Four = 1 << 2,
    Five = Four | One,
}

If I want to know whether one value contains another, I can write a simple extension method using the bitwise logical AND (&) operator. This looks a lot like Enum.HasFlag, but I'm writing it out for a reason. This method needs to know the Enum type, and HasFlag only works on matching enum types. I want a generic solution that works across types:

public static class CountingEnumExtensions
{
    public static bool Contains(this CountingEnum value, CountingEnum target)
    {
        return (value & target) == target;
    }
}

This lends itself to a clean syntax for checking if one Flags value contains another:

if (CountingEnum.Five.Contains(CountingEnum.Four))
{
    // Yep!
}

if (CountingEnum.Four.Contains(CountingEnum.Five))
{
    // Nope!
}

But what if I have another Flags Enum? I could make another extension method each time I want to do this, but that isn't very sustainable. .HasFlag is also no help:

if (CountingEnum.Three.HasFlag(AnotherCountingEnum.One){
     // System.ArgumentException
}

I could just manually use this everywhere, but it isn't very readable for the bitwise non-literate:

if ((SomeEnum.Value & SomeEnum.Target) == SomeEnum.Target)
{
    // Dunno!
}

But is there a general solution? The following will not compile, of course, but it conveys the idea of what I'd like to have:

public static class EnumExtensions
{
    public static bool Contains(this Enum value, Enum target)
    {
        // Cannot apply operation '&' to operands of
        // type 'System.Enum' and 'System.Enum'
        return (value & target) == target;
    }
}

Is there a general solution to perform a bitwise logical AND against any two Flags Enum values of matching type?

bopapa_1979
  • 8,949
  • 10
  • 51
  • 76
  • 2
    This is what `value.HasFlag(target)` does, its name suggests it only works for a single flag but you can pass combinations as target too. It's not efficient though, and it has the "wrong" name so maybe still not super clear. Is that sufficient for your purposes? – harold May 06 '22 at 21:12
  • Related question: https://stackoverflow.com/questions/53636974/c-sharp-method-to-combine-a-generic-list-of-enum-values-to-a-single-value My answer to it is completely generic and doesn't change the underlying type. It suffers from creating a new exprerssion and delegate each time, but that's not hard to fix. – madreflection May 06 '22 at 21:27
  • @harold this doesn't work across different Enum types. The whole idea is to operate directly against Enum or cast to some integral numeric type so that this works across enums that represent the same values but have differing types. We get this alot (for better or worse) with DDD domains where each domain gets some version of the Flags, either on Aggregates or Value Object. Since they don't all reference the same type, I don't think this works. CountingEnum.Three.HasFlag(AnotherCountingEnum.One) <-- System.ArgumentException. – bopapa_1979 May 12 '22 at 16:41

1 Answers1

2

If their signs are not important, you can try to unsigned integer type conversion.

public static bool Contains(this Enum value, Enum target)
{
    uint y = Convert.ToUInt32(value);
    uint z = Convert.ToUInt32(target)
    return (y & z) == z;
}
  • 1
    We wound up going with signed int64 instead of uint32, but this was the exact correct approach. I have tests that confirm this works between all integral-number based Enum types, including conversions between meaningfully identical enums of different concrete types. Perfect, thank you! – bopapa_1979 May 13 '22 at 16:32
  • 1
    @bopapa_1979 I'm glad to have helped. – Soner from The Ottoman Empire May 13 '22 at 17:41