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
checkedoperator is defined without a regular counterpart, the compiler will report an error