1

Given this enum:

[Flags]
public enum Result
{
    None = 0,
    Draw = 1 << 0,
    Win = 1 << 1,
    Loss = 1 << 2,
    NotLost = Draw | Win,
    NotWon = Draw | Loss,
    Any = Draw | Win | Loss
}

and this class:

public class Match
{
    public Result Result { get; }

    ...
}

How can I get a list of object Match where property Result has the flag NotWon out of a List<Match> matches?

I've tried matches.FindAll(m => m.Result.HasFlag(Result.NotWon)); but it's always empty, while matches.FindAll(m => m.Result.HasFlag(Result.Loss)); works as intended.

BeNes
  • 88
  • 7
  • 1
    Seems to work [just fine](https://rextester.com/POKG94747). I suspect that your logic is incorrect though. Note that `Result.NotWon.HasFlag(Result.Loss)` would return true, not the other way around (i.e., `Result.Loss.HasFlag(Result.NotWon)` returns false). Are you sure there's no confusion about this? – 41686d6564 stands w. Palestine Aug 20 '20 at 16:00
  • I'm new to using ```[Flags] enum```, so my thought process was to use NotLost, NotWon and Any as some kind of helper when looking for results. The code itself will only create matches using Draw, Loss or Win. Changed your code example to represent what I was thinking: https://rextester.com/UUHN12601 – BeNes Aug 20 '20 at 16:14
  • Yes, that's what I suspected. No, that's not how flags work. Check Joe's answer below; he explains it in more detail. – 41686d6564 stands w. Palestine Aug 20 '20 at 16:16
  • A side note: while HasFlag works fine, it is better to user `(value & Result.NotWon) == Result.NotWon` as this avoid [boxing](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing) the value – Klaus Gütter Aug 20 '20 at 18:57

1 Answers1

2

The | operator is a bitwise OR, not a logical OR. Thus NotWon means "the value where both Draw and Loss bits are set".

NotWon = Draw | Loss = (1 << 0) | (1 << 2) = 1 | 4 = 0b0001 | 0b0100 = 0b0101 = 5.

Since Enum.HasFlag is a shorthand for this & flag == flag, it will only evaluate to true if m.Result has both bits set. It will evaluate to false if only one of the bits are set.

There are two ways to get what you want that I can think of:

  1. Remove the Not... cases from your enum. Just check if the flag you don't want is set, and then invert the result, i.e. matches.FindAll(m => !m.Result.HasFlag(Result.Win)).
  2. Use the Not... cases as masks and check for non-zero, e.g. matches.FindAll(m => (m.Result & Result.NotWon) != 0).
Joe Sewell
  • 6,067
  • 1
  • 21
  • 34
  • Ah, got it now, thanks! Solution works well, just a little sidequestion whiche doesn't need it's own thread I think: I've removed all the ```Not...``` and ```Any``` cases and everything works as expected, but when I try to get any results not being ```None``` like ```matches.FindAll(m => !m.Result.HasFlag(Result.None));``` it's still returning an empty list. Vice versa it's giving me all results when looking for the ones being ```None```. Any suggestions? – BeNes Aug 20 '20 at 19:01
  • 1
    Just check for equality with None, `m => m.Result != Result.None`. Because `anything & 0` is 0, so the `HasFlag` method can't really handle enums with value `0` correctly. This is the unfortunate thing about C#'s flag-style enums, is that you have to be a bit more aware of the way the binary math works. – Joe Sewell Aug 20 '20 at 20:43