C# checked and unchecked — integer overflow explained

Integer arithmetic in C# silently wraps around on overflow by default — adding 1 to int.MaxValue gives int.MinValue, not an error. The checked keyword turns overflow into an OverflowException. The unchecked keyword (the default) explicitly opts out.

The Overflow Problem

C# Example Code
int max = int.MaxValue; // 2,147,483,647
int result = max + 1;

Console.WriteLine(result); // -2147483648 — silent wrap-around!

No exception, no warning — this is the default (unchecked) behavior.

checked — Throw on Overflow

C# Example Code
int max = int.MaxValue;

try
{
    int result = checked(max + 1); // throws OverflowException
    Console.WriteLine(result);
}
catch (OverflowException ex)
{
    Console.WriteLine($"Overflow: {ex.Message}");
    // Overflow: Arithmetic operation resulted in an overflow.
}

Checked Block

Apply checked to a whole block of code:

C# Example Code
checked
{
    int a = int.MaxValue;
    int b = a + 1;   // throws OverflowException
    int c = a * 2;   // also throws
}

unchecked — Explicit Wrap-Around

Use unchecked when you intentionally want wrap-around behavior (e.g., hash code computation, bit manipulation):

C# Example Code
unchecked
{
    int a = int.MaxValue;
    int b = a + 1;
    Console.WriteLine(b); // -2147483648 — wrap-around, no exception
}

// unchecked expression
int hash = unchecked(17 * 31 + someValue);

Scope Rules

checked/unchecked apply to the immediately-enclosed expressions and blocks, not to method calls inside them:

C# Example Code
int Multiply(int a, int b) => a * b; // this runs unchecked regardless

checked
{
    int result = Multiply(int.MaxValue, 2); // overflow happens inside Multiply — not caught here!
    // To catch it, the multiplication itself must be inside checked
}

Compiler Setting — /checked

You can make checked the default for the entire project by adding to your .csproj:

<PropertyGroup>
  <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
</PropertyGroup>

With this set, all arithmetic is checked by default. Use unchecked to opt out for performance-sensitive or intentionally-wrapping code.

All Integer Types Affected

C# Example Code
// byte, sbyte, short, ushort, int, uint, long, ulong all overflow
byte b = byte.MaxValue; // 255
Console.WriteLine(unchecked((byte)(b + 1))); // 0 — wraps

checked
{
    byte overflow = checked((byte)(b + 1)); // OverflowException
}

Conversion Overflow

checked also affects explicit casts between numeric types:

C# Example Code
int big = 300;

// Unchecked (default) — silently truncates
byte truncated = (byte)big;
Console.WriteLine(truncated); // 44 — silent data loss

// Checked — throws instead of silently losing data
try
{
    byte safe = checked((byte)big);
}
catch (OverflowException)
{
    Console.WriteLine("Value 300 does not fit in a byte"); // prints this
}

When to Use Each

ScenarioUse
Safety-critical financial/medical calculationschecked
Validating user-supplied numeric inputchecked
Hash code computation (GetHashCode)unchecked
Low-level bit manipulationunchecked
Performance-sensitive inner loopsunchecked (default)
Project-wide overflow detection during development/checked compiler flag

Real-World Example: Safe Counter

C# Example Code
public class SafeCounter
{
    private int _count;

    public void Increment()
    {
        try
        {
            _count = checked(_count + 1);
        }
        catch (OverflowException)
        {
            throw new InvalidOperationException(
                $"Counter exceeded maximum value ({int.MaxValue}).");
        }
    }

    public int Value => _count;
}

checked vs Try-Catch for TryParse

Don't confuse overflow with parse errors. Use int.TryParse for string → int conversion failures; use checked for overflow during arithmetic:

C# Example Code
// ✅ TryParse handles malformed strings, not arithmetic overflow
if (!int.TryParse(userInput, out int value))
{
    Console.WriteLine("Not a valid integer");
    return;
}

// ✅ checked handles the calculation that follows
try
{
    int doubled = checked(value * 2);
    Console.WriteLine(doubled);
}
catch (OverflowException)
{
    Console.WriteLine("Result too large for int — use long");
}

checked/unchecked Do Not Apply to long + Large Values

When you need values beyond int.MaxValue (2.1 billion), use long (up to ~9.2 × 10¹⁸) or decimal/BigInteger for arbitrary precision:

C# Example Code
long bigResult = (long)int.MaxValue + 1;
Console.WriteLine(bigResult); // 2147483648 — correct, no overflow