HOWTO · Csharp

How to Use a Bitmask in C#

In this article, we'll learn how to use bitmask in C#.

On this page

A bitmask is an integer whose individual bits represent independent flags. For example, a permission value can store read, write, and execute settings in one number. C# bitwise operators let you test, extract, set, clear, toggle, invert, and shift those bits. Use named masks so each operation says which flag it affects.

Understand bitmasks and bitwise operators

Each bit is either 0 or 1. The operators compare corresponding bits: AND (&) produces 1 only when both inputs are 1; OR (|) produces 1 when either input is 1; XOR (^) produces 1 when the inputs differ; and NOT (~) flips every bit in its integer operand. The shift operators (<< and >>) move bits by a chosen number of positions.

Operation Meaning for each bit
x & y Keep a bit only when it is set in both values.
`x y`
x ^ y Set a bit when exactly one value has it set.
~x Flip every bit of the integer representation.

A mask is a value with 1 in the positions of interest and 0 elsewhere. Prefer named constants such as Read = 1 and Write = 2; these values select separate bit positions.

Test and extract bits with &

To test a flag, AND the value with its mask. The flag is present when the result is nonzero: if ((permissions & Read) != 0). This works because all other positions are cleared by the mask. To extract several selected bits, use a mask containing those positions; the result retains only the selected bits. For example, permissions & (Read | Write) extracts the read and write flags together.

Set bits with |

OR a value with a flag mask to turn those bits on while preserving the other bits. The compound assignment permissions |= Write is shorthand for assigning permissions | Write back to permissions. Setting an already-set flag leaves it set, so this operation is suitable when the goal is to ensure a flag is enabled.

Toggle bits with ^

XOR with a mask flips only the positions where the mask has 1: permissions ^= Execute. A set bit becomes clear and a clear bit becomes set. This is useful for a deliberate toggle, but it is not an idempotent way to ensure a flag is on; applying it twice restores the original value.

Invert bits with ~

NOT flips all bits in the integer type, including positions that are not part of the flags your application uses. To clear a known flag, combine AND with the complemented mask: permissions &= ~Write. The other flag positions remain unchanged. If you need to display an inverted value as a fixed-width bit string, mask it to that width first; the numeric result of ~value itself is not limited to the visible flags.

Shift bits with << and >>

A left shift moves bits toward higher positions. For nonnegative values where the shifted result fits, value << count has the effect of multiplying by a power of two. A right shift moves bits toward lower positions; for nonnegative values, it discards the lowest shifted bits. For signed negative values, C#’s right shift preserves the sign bit, so do not describe every right shift as inserting zeroes. Use an unsigned type when the task requires logical right-shift behavior, and choose a shift count appropriate for the integer type.

Shifts are also a concise way to construct masks: 1 << 0, 1 << 1, and 1 << 2 select successive bit positions for small nonnegative counts. Named constants are usually easier to read in application code.

Combine flags in a practical example

This program starts with no permissions, enables read and write, checks and extracts those flags, clears write, then toggles execute. Each printed value describes the state after the corresponding operation. The example uses a complete Main entry point and decimal masks.

Run this example:

using System;

class Program
{
    static void Main()
    {
        const int Read = 1;
        const int Write = 2;
        const int Execute = 4;
        int permissions = 0;

        permissions |= Read | Write;
        Console.WriteLine((permissions & Read) != 0);
        Console.WriteLine(permissions & (Read | Write));

        permissions &= ~Write;
        permissions ^= Execute;
        Console.WriteLine(permissions);
    }
}

The first operation sets two flags with OR. The test prints whether Read is present, and the extraction prints the selected numeric bits. Clearing with &= ~Write removes only the write bit; XOR then toggles execute. Keep masks and stored values in compatible integer types, and use named flags to make the intended positions clear.