Bitwise operations are important when dealing with permissions. In this scenario, permissions are designed as flags — a single variable can hold one or multiple permissions, all described in an enum decorated with [Flags].
[Flags]
public enum Flags : long
{
T1_CanDelete = 2 << 0,
T1_CanEdit = 2 << 1,
T2_CanDelete = 2 << 2
}Or Operation
Combine multiple permissions with the | operator. Each pair of bits is set to 1 if either bit is 1:
// Combine permissions with OR
var op1 = Flags.T1_CanDelete | Flags.T1_CanEdit;
// Visualize bits
Console.WriteLine(Convert.ToString((int)Flags.T1_CanDelete, 2).PadLeft(8, '0')); // 00000010
Console.WriteLine(Convert.ToString((int)Flags.T1_CanEdit, 2).PadLeft(8, '0')); // 00000100
Console.WriteLine(Convert.ToString((int)op1, 2).PadLeft(8, '0')); // 00000110And Operation
To check if a flag is set, use the bitwise & operator rather than HasFlag — it checks if both bits are 1 and is more performant:
// HasFlag — convenient but less performant
Console.WriteLine(op1.HasFlag(Flags.T1_CanDelete));
// Bitwise AND — more performant
bool hasFlag = (op1 & Flags.T1_CanDelete) != 0;
// Visualize: only 1 if BOTH bits are 1
Console.WriteLine(Convert.ToString((int)op1, 2).PadLeft(8, '0')); // 00000110 (op1)
Console.WriteLine(Convert.ToString((int)Flags.T1_CanDelete, 2).PadLeft(8, '0')); // 00000010 (flag)
Console.WriteLine(Convert.ToString((int)(op1 & Flags.T1_CanDelete), 2).PadLeft(8, '0')); // 00000010 (result)Removing a Flag — Wrong Way
XOR (^) cannot be used to reliably remove a flag — it acts as a toggle. Applying it twice with the same flag restores the original value:
// XOR acts as a toggle — applying it twice restores the original value
Console.WriteLine(Convert.ToString((int)Flags.T1_CanDelete, 2).PadLeft(8, '0')); // 00000010
Console.WriteLine(Convert.ToString((int)op1, 2).PadLeft(8, '0')); // 00000110
op1 = op1 ^ Flags.T1_CanDelete;
Console.WriteLine(Convert.ToString((int)op1, 2).PadLeft(8, '0')); // 00000100 (toggled off)
op1 = op1 ^ Flags.T1_CanDelete;
Console.WriteLine(Convert.ToString((int)op1, 2).PadLeft(8, '0')); // 00000110 (toggled back on)Removing a Flag — Correct Way
Use the bitwise complement operator ~ to invert all bits of the target flag, then & to mask it out. The inverted flag ensures the target bit becomes 0 in the result:
// Use bitwise complement (~) to invert flag bits, then AND to clear them
op1 = Flags.T2_CanDelete | Flags.T1_CanEdit | Flags.T2_CanDelete;
Console.WriteLine(Convert.ToString((int)op1, 2).PadLeft(8, '0')); // 00001100 (op1)
Console.WriteLine(Convert.ToString((int)Flags.T2_CanDelete, 2).PadLeft(8, '0')); // 00001000 (flag)
Console.WriteLine(Convert.ToString((int)~Flags.T2_CanDelete, 2).PadLeft(8, '0')); // 11110111 (masked)
Console.WriteLine(Convert.ToString((int)((~Flags.T2_CanDelete) & op1), 2).PadLeft(8, '0')); // 00000100 (result)