21

I have this scenario where user has its role

NormalUser
Custodian
Finance

both Custodian and Finance is a SuperUser

How can i check if a role Custodian is a SuperUser

this is my sample code..

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication3
{
    public enum Role
    {
        NormalUser = 0,
        Custodian = 1,
        Finance = 2,
        SuperUser = Custodian | Finance,
        All = Custodian | Finance | NormalUser
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Normal: " + Convert.ToInt32(Role.NormalUser));            
            Console.WriteLine("Custodian: " + Convert.ToInt32(Role.Custodian));
            Console.WriteLine("Finance: " + Convert.ToInt32(Role.Finance));            
            Console.WriteLine("SuperUser: " + Convert.ToInt32(Role.SuperUser));
            Console.WriteLine("All: " + Convert.ToInt32(Role.All));

            Console.WriteLine();
            Console.WriteLine("Normal User is in All: {0}", Role.NormalUser == Role.All);
            Console.WriteLine("Normal User is not a SuperUser: {0}", Role.NormalUser != Role.SuperUser);
            Console.WriteLine("Normal User is not a Custodian: {0}", Role.NormalUser != Role.Custodian);

            Console.WriteLine();
            Console.WriteLine("Custodian is in All: {0}", Role.Custodian == Role.All);
            Console.WriteLine("Custodian is a SuperUser: {0}", Role.Custodian == Role.SuperUser);
            Console.WriteLine("Custodian is a NormalUser: {0}", Role.Custodian == Role.NormalUser);

            Console.WriteLine();
            Console.WriteLine("Finance is in All: {0}", Role.Finance == Role.All);
            Console.WriteLine("Finance is a SuperUser: {0}", Role.Finance == Role.SuperUser);
            Console.WriteLine("Finance is a NormalUser: {0}", Role.Finance == Role.NormalUser);

            Console.ReadLine();
        }
    }
}

and this is the result if we run it

Normal: 0
Custodian: 1
Finance: 2
SuperUser: 3
All: 3

Normal User is in All: False
Normal User is not a SuperUser: True
Normal User is not a Custodian: True

Custodian is in All: False
Custodian is a SuperUser: False
Custodian is a NormalUser: False

Finance is in All: False
Finance is a SuperUser: False
Finance is a NormalUser: False

i am expecting that a

Custodian is in All: True,
Custodian is a SuperUser: True,
Finance is in All: True,
Finance is a SuperUser: True,
Normal User is in All: True

Vincent Dagpin
  • 3,581
  • 13
  • 55
  • 85

4 Answers4

61

Enum.HasFlag is what you want to use

Console.WriteLine("Custodian is in All: {0}", Role.All.HasFlag(Role.Custodian));

Just noticed that your enum should be defined like this with the Flags attribute and values spaced out by powers of 2

[Flags]
public enum Role
{
    NormalUser = 1,
    Custodian = 2,
    Finance = 4,
    SuperUser = Custodian | Finance,
    All = Custodian | Finance | NormalUser
}

The reason powers of 2 are used for flagged enums is that each power of 2 represents a unique bit being set in the binary representation:

NormalUser = 1 = 00000001
Custodian  = 2 = 00000010
Finance    = 4 = 00000100
Other      = 8 = 00001000

Because each item in the enum has a unique bit set this allows them to be combined by setting their respective bits.

SuperUser  = 6 = 00000110 = Custodian + Finance
All        = 7 = 00000111 = NormalUser + Custodian + Finance
NormOther  = 9 = 00001001 = NormalUser + Other

Notice how each 1 in the binary form lines up with the bit set for the flag in the section above.

David Ewen
  • 3,632
  • 1
  • 19
  • 30
  • What is reason for spaced out by powers of 2 values. I know that is how you are supposed to do it but not sure as why? – DonO Jan 20 '17 at 21:59
  • 1
    @DonO just updated answer to include explanation of why powers of 2 are used, it was a little big to cover in a comment. Hopefully it's useful / makes sense. – David Ewen Jan 23 '17 at 04:20
20

Check out What does the [Flags] Enum Attribute mean in C#? for a more thorough explanation.

A "safer" way to declare flags is to use bit-shifting to ensure there's no overlap (as mentioned by @DaveOwen's answer) without figuring out the math yourself:

[Flags]
public enum MyEnum
{
    None   = 0,
    First  = 1 << 0,
    Second = 1 << 1,
    Third  = 1 << 2,
    Fourth = 1 << 3
}

There's also Enum.HasFlag (possibly newer .NET than OP) for checking, rather than Expected & Testing == Expected

Community
  • 1
  • 1
drzaus
  • 24,171
  • 16
  • 142
  • 201
2

You can add the Flag-attribute to the Enum

[Flags]
public enum Role
{
    NormalUser,
    Custodian,
    Finance,
    SuperUser = Custodian | Finance,
    All = Custodian | Finance | NormalUser
}

Then you can check for a role with this expression:

 Role testRole = Role.Finance
 if(testRole & Role.SuperUser == Role.SuperUser){
      //testRole is SuperUser
 }
Turbofant
  • 517
  • 11
  • 24
  • 7
    You still need to use powers of two. Otherwise it won't work. That's in the documentation: https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ms229062(v=vs.100) – André Mantas Jan 24 '19 at 11:01
2

I think this might be a duplicate of How do you pass multiple enum values in C#?

Where the & bitmask can do the trick.

((Role.NormalUser & Role.All) == Role.NormalUser)

Inspecting this closer you will get the following:

0b0 & 0b11 == 0b0

However if you lets say want to check if the SuperUser is in finance you will get the following:

((Role.SuperUser & Role.Finance) == Role.Finance)

This will evaluate to:

0b11 & 0b10 == 0b10
Community
  • 1
  • 1
  • I dont get why. Isnt `a & b == c` just `a == c && b == c`? If so, why not `a == c`? Your `Role.All` looks wrong in here for me. Not saying your are wrong. Just saying I cant get why... – C4d Nov 02 '16 at 12:35