tags:

views:

1540

answers:

9

Let's say I have this enum:

[Flags]
enum Letters
{
     A = 1,
     B = 2,
     C = 4,
     AB = A | B,
     All = A | B | C,
}

To check if for example AB is set I can do this:

if((letter & Letters.AB) == Letters.AB)

Is there a simpler way to check if any of the flags of a combined flag constant are set than the following?

if((letter & Letters.A) == Letters.A || (letter & Letters.B) == Letters.B)

Could you for example swap the & with something?

Not too stable when it comes to binary stuff like this...

A: 
if((int)letter != 0) { }
Lee
You might the same mistake as I did - he wants to check if A or B is set but ignore C.
Daniel Brückner
You don't need the cast if you're checking the enum against 0.
Stevo3000
This would check if any of all of them was set, not if any of a combined enum was set.
Svish
+1  A: 

How about

if ((letter & Letters.AB) > 0)

?

Jakob Christensen
Yes! This will filter on the A and B values, and ignore if C is included. So if it is >0, it is also A or B or AB.
awe
This doesn't 100% work with signed values. != 0 is better than > 0 for this reason.
Stevo3000
+3  A: 

If it really annoys you, you can write a function like that:

public bool IsSet(Letters value, Letters flag)
{
    return (value & flag) == flag;
}

if (IsSet(letter, Letters.A))
{
   // ...
}

// If you want to check if BOTH Letters.A and Letters.B are set:
if (IsSet(letter, Letters.A & Letters.B))
{
   // ...
}

// If you want an OR, I'm afraid you will have to be more verbose:
if (IsSet(letter, Letters.A) || IsSet(letter, Letters.B))
{
   // ...
}
DrJokepu
The line `return (value
Fredrik Mörk
you need to cast to a long first...
Thomas Levesque
This does not help him with understanding of binary operations...
awe
awe: The question was not about binary operations, the question was about simplifying the syntax of bitmask-related operations in C#. There are plenty of excellent binary operation related questions and answers on stackoverflow already, there is no need to repost them everywhere.
DrJokepu
I should recommend that those unfamiliar with binary operations get familiar, as the scaffolding to hide it above actually makes things much less readable in my opinion. Of course my 'raw' solution below is currently not doing so well compared to the score of this solution, so people are voting their preferences and I have to respect that ;-)
Will
+3  A: 

If you want to know if letter has any of the letters in AB you must use the AND & operator. Something like:

if ((letter & Letters.AB) != 0)
{
    // Some flag (A,B or both) is enabled
}
else
{
    // None of them are enabled
}
yeyeyerman
Svish
Also if I introduced a `Letters.None`, I assume you could swap that with the `0` for a less compare-with-magic-number look?
Svish
Of course. But I don't think the AND comparison with 0 can be thought as a magic number strictly.
yeyeyerman
+1  A: 

Would this work for you?

if ((letter & (Letters.A | Letters.B)) != 0)

Regards,

Sebastiaan

Sebastiaan Megens
You don't need the cast if you're checking the enum against 0.
Stevo3000
+2  A: 

To check if for example AB is set I can do this:

if((letter & Letters.AB) == Letters.AB)

Is there a simpler way to check if any of the flags of a combined flag constant are set than the following?

This checks that both A and B are set, and ignores whether any other flags are set.

if((letter & Letters.A) == Letters.A || (letter & Letters.B) == Letters.B)

This checks that either A or B is set, and ignores whether any other flags are set or not.

This can be simplified to:

if(letter & Letters.AB)

Here's the C for binary operations; it should be straightforward to apply this to C#:

enum {
     A = 1,
     B = 2,
     C = 4,
     AB = A | B,
     All = AB | C,
};

int flags = A|C;

bool anything_and_a = flags & A;

bool only_a = (flags == A);

bool a_and_or_c_and_anything_else = flags & (A|C);

bool both_ac_and_anything_else = (flags & (A|C)) == (A|C);

bool only_a_and_c = (flags == (A|C));

Incidentally, the naming of the variable in the question's example is the singular 'letter', which might imply that it represents only a single letter; the example code makes it clear that its a set of possible letters and that multiple values are allowed, so consider renaming the variable 'letters'.

Will
Wouldn't `anything_and_a`, `a_and_or_c_and_anything_else` and `both_ac_and_anything_else` always be true? or am I missing something here?
Svish
Will
Hm, some of those ends up as numbers and not boolean in C# though. How would you convert them to boolean?
Svish
Its not implicitly converted for you? Zero is equiv to 'false', and all other values are 'true'.
Will
A: 

You could just check if the value is not zero.

if ((Int32)(letter & Letters.AB) != 0) { }

But I would consider it a better solution to introduce a new enumeration value with value zero and compare agains this enumeration value (if possible because you must be able to modify the enumeration).

[Flags]
enum Letters
{
    None = 0,
    A    = 1,
    B    = 2,
    C    = 4,
    AB   =  A | B,
    All  = AB | C
}

if (letter != Letters.None) { }

UPDATE

Missread the question - fixed the first suggestion and just ignore the second suggestion.

Daniel Brückner
You don't need the cast if you're checking the enum against 0.
Stevo3000
A: 

There are two aproaches that I can see that would work for checking for any bit being set.

Aproach A

if (letter != 0)
{
}

This works as long as you don't mind checking for all bits, including non-defined ones too!

Aproach B

if ((letter & Letters.All) != 0)
{
}

This only checks the defined bits, as long as Letters.All represents all of the possible bits.

For specific bits (one or more set), use Aproach B replacing Letters.All with the bits that you want to check for (see below).

if ((letter & Letters.AB) != 0)
{
}
Stevo3000
You might the same mistake as I did - he wants to check if A or B is set but ignore C.
Daniel Brückner
+6  A: 

I use extension methods to write things like that :

if (letter.IsFlagSet(Letter.AB))
    ...

Here's the code :

public static class EnumExtensions
{
    private static void CheckIsEnum<T>(bool withFlags)
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName));
        if (withFlags && !Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))
            throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName));
    }

    public static bool IsFlagSet<T>(this T value, T flag) where T : struct
    {
        CheckIsEnum<T>(true);
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flag);
        return (lValue & lFlag) != 0;
    }

    public static IEnumerable<T> GetFlags<T>(this T value) where T : struct
    {
        CheckIsEnum<T>(true);
        foreach (T flag in Enum.GetValues(typeof(T)).Cast<T>())
        {
            if (value.IsFlagSet(flag))
                yield return flag;
        }
    }

    public static T SetFlags<T>(this T value, T flags, bool on) where T : struct
    {
        CheckIsEnum<T>(true);
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flags);
        if (on)
        {
            lValue |= lFlag;
        }
        else
        {
            lValue &= (~lFlag);
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }

    public static T SetFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, true);
    }

    public static T ClearFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, false);
    }

    public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct
    {
        CheckIsEnum<T>(true);
        long lValue = 0;
        foreach (T flag in flags)
        {
            long lFlag = Convert.ToInt64(flag);
            lValue |= lFlag;
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }

    public static string GetDescription<T>(this T value) where T : struct
    {
        CheckIsEnum<T>(false);
        string name = Enum.GetName(typeof(T), value);
        if (name != null)
        {
            FieldInfo field = typeof(T).GetField(name);
            if (field != null)
            {
                DescriptionAttribute attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
                if (attr != null)
                {
                    return attr.Description;
                }
            }
        }
        return null;
    }
}
Thomas Levesque
Although kind of overkill to this question, thanks for posting. Might come in handy some day =)
Svish