6

In C# (using Unity working on a game) I have an enum with the [Flag] attribute. I have instantiated it twice. I would like a way to compare the two enums. Specifically if enum A (which will have multiple flags) contains a flag from enum B (which will only ever be assigned a single flag).

I am not trying to compare a single instantiated enum to a single flag (this has been answered multiple times).

I suspect I could do this by dumping values with GetValue and comparing those values on a foreach loop, but it seems like there should be a more direct way to compare.

public enum AbilityType
{
    None = 0,
    Pierce = 1<<1,
    Blunt = 1<<2,
    Slash = 1<<3,
    Water = 1<<4,
    // etc.
};


public class Ability : MonoBehaviour
{
    public AbilityType abilityType;
}

public class AbilitiedObject : StatisticalObject
{
    public AbilityType resistances;

    protected override void Awake()
    {
        base.Awake();
        resistances = AbilityType.Pierce | AbilityType.Water;
    }

    public void TakeDamage(int damageAmount, AbilityType abilityType)
    {
        if( ) // Check if resistances contains abilityType's flag here
        {
            print("You are resistance to this damage type");
        }
        else
        {
            // Player takes damage
        }
    }
}

I'd like the above code to check if resistances contains the flag from abilityType. In the example above, the attack in question will pass it's abilityType in. If that type is water or pierce, it should print the resistance statement. If it's another type, it should deal damage as normal.

derHugo
  • 83,094
  • 9
  • 75
  • 115
George Barron
  • 99
  • 2
  • 3
  • 10
  • 1
    Would this [SO Article](https://stackoverflow.com/questions/93744/most-common-c-sharp-bitwise-operations-on-enums) be of any use to you? Sounds like you want bitwise operation. Something like this `if(resistances & abilityType == abilityType)`...not at computer. – Simon Wilson Jun 16 '19 at 17:53
  • Not trying to sound bossy but flags is widely documented on the internet. You can google "flag c#" and you are likely to find a quick answer. – Everts Jun 16 '19 at 17:54
  • You can use bitwise AND and compare to not zero to see if that flag is set – Sami Kuhmonen Jun 16 '19 at 17:54
  • @Everts if it's so easy, please feel free to help me out here and Google it. I've been Googling for two hours without finding my solution here. It is easy enough to compare a single flags enum to a single potential flag, but I haven't found a single example online of comparing multiple flagged enums to each other. – George Barron Jun 16 '19 at 17:57
  • But isn't this exactly the "check if flag is set" question that has been answered multiple times? How is it different? – harold Jun 16 '19 at 17:58
  • @SimonWilson unfortunately that statement only evaluates true if they contain identical information. In this case, I need it to evaluate true if even if resistances contains more flags than the one in abilityType. – George Barron Jun 16 '19 at 17:58
  • 1
    Possible duplicate of [Most common C# bitwise operations on enums](https://stackoverflow.com/questions/93744/most-common-c-sharp-bitwise-operations-on-enums). Literally the top answer has what you want. – Draco18s no longer trusts SE Jun 16 '19 at 17:59
  • @harold Because I'm not checking for a singular flag. I'm checking two flagged enums against each other to see if one is contained in the other. – George Barron Jun 16 '19 at 17:59
  • Yes, that's what this does: `public static bool Has(this T type, T value) { return (type & value) == value; }` – Draco18s no longer trusts SE Jun 16 '19 at 18:00
  • @Draco18s No, that's not my solution... again. I'm looking to compare TWO enums, not ONE enum and a flag. – George Barron Jun 16 '19 at 18:06
  • 1
    @DrakeBarron There is no difference whatsoever between "two enums" and "one enum and a flag". – GSerg Jun 16 '19 at 18:11
  • If you're trying to say that *both* `resistances` and `abilityType` have two (or more) flags applied to them, now you need to decide "does it take only one resistance to be resistant, or does it take both?" – Draco18s no longer trusts SE Jun 16 '19 at 18:16
  • 3
    By the way the difference between the form `(a & b) != 0` and the form `(a & b) == b` is that the first checks whether *any* of `b`'s flags are set in `a`, and the second checks whether *all* of them are set. – harold Jun 16 '19 at 18:26

2 Answers2

17

What you want is to abuse, as stated in the comments and other answers, is the bitwise & operator to compare as enums are (by default) based on the int type. However since .NET 4.0 there has been the addition of Enum.HasFlag extension method that does exactly this and increases readability.

// These two are semantically equal

if (resistance.HasFlag(abilityType))
{
    print("You are resistance to this damage type");
}

if ((resistance & abilityType) != 0)
{
    print("You are resistance to this damage type");
}

Behind the scenes of this is explained by numerous others. Can personally recommend Alan Zucconi's Enum, Flags and bitwise operators

Short version is that the bitwise AND (&) operator gives a result of where two values "match" in the sense that they're both active, bit by bit. One way to think of it is as a selective filter. To check if a value A of unknown flag set contains a specific flag B, you use the filter which only allows flag B to pass through and check if anything got through, where anything is represented as anything else than zero. The filter is simply the same as the flag you wish to look for. Therefore the expression becomes (A & B) != 0. Parentheses to force override order of operations since != has higher precedence than &.

Applejag
  • 1,068
  • 10
  • 17
  • ``Enum.HasFlag()`` works properly with enumeration types that are marked with the ``FlagsAttribute`` attribute only – ZENIT Jan 14 '20 at 16:38
  • **Increases Readability** ... in some cases, is a poor standard. *If you like syntactic sugar* is more appropriate. If you can't *read & **comprehend*** `(A & B) != 0`, I'm not sure programming is for you. :/ ... otherwise, good explanation! +1. – IAbstract Dec 24 '22 at 12:27
2

Here's what you need:

if ((resistances & abilityType) != 0)
{
    print("You are resistance to this damage type");
}
...
Dest
  • 459
  • 2
  • 7
  • 1
    In this case, that seems correct enough. In a more general sense, values may exist that _combine_ flags. Here, `resistances & AbilityType.SlashingWater` would be nonzero if `resistances` contains `Water` but not `Slashing`, so you would return a false positive. The question (usually) asked is: "Does `resistances` _contain_ the flag(s) specified by `abilityType`?" The question answered by your implementation is: "Does `resistances` contain _at least one of_ the flags specified by `abilityType`?" To answer the former, more common question: `(resistances & abilityType) == abilityType` – Timo Oct 07 '21 at 15:45