Perrier
Perrier

Reputation: 2827

C# flag enum toggle by value or name

I have a Flags enum and would like to toggle some values by its (long) value.

[Flags]
public enum Permissions : long
{
    Value0 = 0,
    Value1 = 0x1,
    Value2 = 0x2,
    Value3 = 0x4,
    //etc
}

I store the actual permission in the database by EF using a long field, eg:

   public Permissions UsersPermissions { get; set; }

From the client side I get values one by one to toggle the proper value by enum, so if the database field contains

`Value1 | Value2 | Value3`

and I get a 4 I have to remove Value3 from the user's permissions.

I have tried usersPermissions |= permissionValue but it says I can not use the |= operator on long and Permission Types.

Upvotes: 0

Views: 1428

Answers (3)

Kevin Anderson
Kevin Anderson

Reputation: 7010

From what I can tell, your input is a number, and you want it to be a Permissions. Well you can literally cast it, even if it's not one of the discrete values. That will even "just figure out" if it's a combination of one of the flag values. C# enumerations are pretty smart with their casts that way.

Here's a sampling of code to do what you want

[Flags]
public enum TestEnum : long
{
    ValueNone = 0,  // You can't really "have" a Value0, it's an absence of values
    Value1 = 0x1,
    Value2 = 0x2,
    Value3 = 0x4,
    Value4 = 0x8
}

foreach(var enumVal in Enum.GetValues(typeof(TestEnum)))
{
    Console.WriteLine("val is: {0} long is: {1}", enumVal, (long)enumVal);
}

{
    Console.WriteLine("Start with 1 and 3, remove 3 from number");
    var testEnum = TestEnum.Value1 | TestEnum.Value3;
    Console.WriteLine("testEnum is: {0}({1})", testEnum, (long)testEnum);
    long testVal = 4;   // TestEnum.Value3
    var removeEnum = (TestEnum)testVal;
    removeEnum = ~removeEnum;
    testEnum &= removeEnum;
    Console.WriteLine("testEnum is: {0}({1})", testEnum, (long)testEnum);
}
{
    Console.WriteLine("Start with 1, 2, 4 and remove 1 and 4 from number");
    var testEnum = TestEnum.Value1 | TestEnum.Value2 | TestEnum.Value4;
    Console.WriteLine("testEnum is: {0}({1})", testEnum, (long)testEnum);
    long testVal = (long)TestEnum.Value1 | (long)TestEnum.Value4;   // Could also have added them
    var removeEnum = (TestEnum)testVal;
    Console.WriteLine("Removing: {0}({1})", removeEnum, (long)removeEnum);
    testEnum &= ~removeEnum;
    Console.WriteLine("testEnum is: {0}({1})", testEnum, (long)testEnum);
}

Here's a way to get the limit of your enum by the value of the last element:

{
    // Check the value to see what the range of the enumeration is
    var lastVal = Enum.GetValues(typeof(TestEnum)).Cast<TestEnum>().Last();
    long limit = (long)lastVal * 2;
    Console.WriteLine("Limit of flags for TestEnum is: " + limit);
    Func<long, bool> testLambda = x => x < limit;
    Console.WriteLine("Value of {0} within limit: {1}", 14, testLambda(14));
    Console.WriteLine("Value of {0} within limit: {1}", 16, testLambda(16));
    Console.WriteLine("Value of {0} within limit: {1}", 200, testLambda(200));
    Console.WriteLine("Value of {0} within limit: {1}", 0, testLambda(0));
    Console.WriteLine("Out of range looks like: " + (TestEnum)17);
    Console.WriteLine("In range looks like: " + (TestEnum)14);
}

Output:

val is: ValueNone long is: 0
val is: Value1 long is: 1
val is: Value2 long is: 2
val is: Value3 long is: 4
val is: Value4 long is: 8
Start with 1 and 3, remove 3 from number
testEnum is: Value1, Value3(5)
testEnum is: Value1(1)
Start with 1, 2, 4 and remove 1 and 4 from number
testEnum is: Value1, Value2, Value4(11)
Removing: Value1, Value4(9)
testEnum is: Value2(2)
Limit of flags for TestEnum is: 16
Value of 14 within limit: True
Value of 16 within limit: False
Value of 200 within limit: False
Value of 0 within limit: True
Out of range looks like: 17
In range looks like: Value2, Value3, Value4

I'll fully admit there may be a better way of doing above, but fundamentally, if you have a [Flags] enum, and thus everything inside it is "valid" to a degree (all combinations fine), just check to see if your value is out of range, and then cast it from the integer type directly to the enum type and call it a day. Once it's your flag type, you CAN use the bitwise operators no problem, so just do that first.

Upvotes: 1

Matthew Watson
Matthew Watson

Reputation: 109557

You have to do some casting to long and Permissions at the appropriate points, like so:

Permissions perms = Permissions.Value1 | Permissions.Value2 | Permissions.Value3;
Console.WriteLine(perms); // Value1, Value2, Value3

long toRemove = 4;
// OR: long toRemove = (long) Permissions.Value3;

perms = (Permissions) ((long)perms & ~toRemove); // Use "& ~X" to remove bits set in X.

Console.WriteLine(perms); // Value1, Value2

The important thing here is that ~toRemove returns the bitwise complement of toRemove which thus gives you a set of bitflags that you can & with the original value in order to turn off all the bits that were set in toRemove.

You have to cast perms to long in order to do this & operation.

Finally, having got the result you need via & you have a long that you must cast back to Permissions to get the correct enum type as the final answer.

Upvotes: 1

Robert
Robert

Reputation: 2523

You have to do an exclusive OR operator on the enumeration. So if you permissions value has:

  • Value1
  • Value2
  • Value3
  • Value4,

and you want to remove Value4 you will have to do this:

var perm = Permissions.Value1 | Permissions.Value2 | Permissions.Value3 | Permissions.Value4;
perm = perm ^ Permissions.Value4;

Upvotes: 0

Related Questions