Skip to content

Checked user-defined operators C# 11.0correctnesstype safety

Define checked versions of user-defined operators for overflow-aware arithmetic.

User-defined operators had no way to distinguish between checked and unchecked contexts. This meant custom numeric types could not throw on overflow the way built-in types do inside a checked block.

C# 11 allows you to define a checked variant of arithmetic and conversion operators. The compiler calls the checked version inside checked contexts and the regular version otherwise.

Code

C#
public readonly struct Fraction
{
    public int Numerator { get; init; }
    public int Denominator { get; init; }

    public static Fraction operator +(Fraction a, Fraction b)
    {
        // Unchecked: wraps on overflow
        return new Fraction
        {
            Numerator = a.Numerator * b.Denominator + b.Numerator * a.Denominator,
            Denominator = a.Denominator * b.Denominator
        };
    }

    public static Fraction operator checked +(Fraction a, Fraction b)
    {
        // Checked: throws OverflowException on overflow
        return new Fraction
        {
            Numerator = checked(a.Numerator * b.Denominator + b.Numerator * a.Denominator),
            Denominator = checked(a.Denominator * b.Denominator)
        };
    }
}
C#
public readonly struct Fraction
{
    public int Numerator { get; init; }
    public int Denominator { get; init; }

    public static Fraction operator +(Fraction a, Fraction b)
    {
        // No way to distinguish checked vs unchecked context
        return new Fraction
        {
            Numerator = a.Numerator * b.Denominator + b.Numerator * a.Denominator,
            Denominator = a.Denominator * b.Denominator
        };
    }
}

Notes

  • Part of the generic math story alongside static interface members
  • Applies to +, -, *, /, unary -, and explicit conversion operators
  • If only a checked operator is defined without a regular counterpart, the compiler will report an error

More information