Flags Enum.HasFlag not Working as Expected for Multiple bit fields

Viewed 323

I have a list of records that contains a flags enum value. I am trying to filter on one or more value based on the flags supplied. The description of the HasFlag method is as follows:

true if the bit field or bit fields that are set in flag are also set in the current instance; otherwise, false.

I would expect given an enum:

[Flags]
public enum MyFlaggedEnum
{
    Unknown = 0,
    First = 1,
    Second = 2,
    Third = 4,
    Fourth = 8,
    Fifth = 16,
    Sixth = 32
}

And a record set containing a mixture of these values, assume that there are at least one record for each of the flags:

When I filter on one value:

var myFilteredList = MyMainList.Where(w => w.flagValue.HasFlag(MyFlaggedEnum.Third));
// myFilteredList returns the correct subset of records

When I filter on multiple values:

var myFilteredListMultiple = MyMainList.Where(w => w.flagValue.HasFlag(MyFlaggedEnum.First | MyFlaggedEnum.Fourth));
// myFilteredList returns an empty set

Am I misusing the HasFlag method; if so, how?

I am looking for a way to quickly filter the results so they can be displayed to the user in a datagridview based on filters they supply at runtime and can be changed at any time. I could loop through them or union multiple sets based on the flag filters requested by the user, but, was looking for a clean way.

EDIT: Whoever closed this question referring to this: How to check if any flags of a flag combination are set? does not understand the question. No, I am not adding First | Second values to my enum!!

2 Answers

I suggest that you eschew HasFlag and just do it directly using bit operations:

var myFilteredListMultiple = MyMainList.Where(w => (w.FlagValue & (MyFlaggedEnum.First | MyFlaggedEnum.Fourth)) != 0);

Or if you have a set of bitflags passed in called, say, mask:

var myFilteredListMultiple = MyMainList.Where(w => (w.FlagValue & mask) != 0);

There's an ambiguity over whether HasFlag should mean "has all" vs "has any"; it is implemented as "has all". But it is also an inefficient (boxing) API, so honestly: it is probably better to simply not use it. For example, you could add your own extension methods:

public static bool HasAny(this MyFlaggedEnum value, MyFlaggedEnum any)
    => (value & any) != 0;
public static bool HasAll(this MyFlaggedEnum value, MyFlaggedEnum all)
    => (value & all) == all;

and use .HasAny(MyFlaggedEnum.First | MyFlaggedEnum.Fourth)

Related