运算符重载 - 预定义的一元、算术、相等和比较运算符

用户定义的类型可以重载预定义的 C# 运算符。 也就是说,当操作数之一或者两个操作数都属于该类型时,该类型可以提供操作的自定义实现。 “可重载运算符”部分显示哪些 C# 运算符可以重载。

使用 operator 关键字声明运算符。 运算符声明必须满足以下规则:

  • 它包括一个public修饰符。
  • 一元运算符有一个输入参数。 二进制运算符有两个输入参数。 在每种情况下,至少有一个参数必须具有类型TT?,其中T是包含运算符声明的类型。
  • 它包括 static 修饰符,复合赋值运算符除外,例如 +=
  • 增量(++)和递减(--)运算符可以作为静态方法或实例方法实现。

以下示例定义一个简化的结构来表示合理数字。 结构重载一些 算术运算符

public struct Fraction
{
    private int numerator;
    private int denominator;

    public Fraction(int numerator, int denominator)
    {
        if (denominator == 0)
        {
            throw new ArgumentException("Denominator cannot be zero.", nameof(denominator));
        }
        this.numerator = numerator;
        this.denominator = denominator;
    }

    public static Fraction operator +(Fraction operand) => operand;
    public static Fraction operator -(Fraction operand) => new Fraction(-operand.numerator, operand.denominator);

    public static Fraction operator +(Fraction left, Fraction right)
        => new Fraction(left.numerator * right.denominator + right.numerator * left.denominator, left.denominator * right.denominator);

    public static Fraction operator -(Fraction left, Fraction right)
        => left + (-right);

    public static Fraction operator *(Fraction left, Fraction right)
        => new Fraction(left.numerator * right.numerator, left.denominator * right.denominator);

    public static Fraction operator /(Fraction left, Fraction right)
    {
        if (right.numerator == 0)
        {
            throw new DivideByZeroException();
        }
        return new Fraction(left.numerator * right.denominator, left.denominator * right.numerator);
    }

    // Define increment and decrement to add 1/den, rather than 1/1.
    public static Fraction operator ++(Fraction operand)
        => new Fraction(operand.numerator++, operand.denominator);

    public static Fraction operator --(Fraction operand) =>
        new Fraction(operand.numerator--, operand.denominator);

    public override string ToString() => $"{numerator} / {denominator}";

    // New operators allowed in C# 14:
    public void operator +=(Fraction operand) =>
        (numerator, denominator ) =
        (
            numerator * operand.denominator + operand.numerator * denominator,
            denominator * operand.denominator
        );

    public void operator -=(Fraction operand) =>
        (numerator, denominator) =
        (
            numerator * operand.denominator - operand.numerator * denominator,
            denominator * operand.denominator
        );

    public void operator *=(Fraction operand) =>
        (numerator, denominator) =
        (
            numerator * operand.numerator,
            denominator * operand.denominator
        );

    public void operator /=(Fraction operand)
    {
        if (operand.numerator == 0)
        {
            throw new DivideByZeroException();
        }
        (numerator, denominator) =
        (
            numerator * operand.denominator,
            denominator * operand.numerator
        );
    }

    public void operator ++() => numerator++;

    public void operator --() => numerator--;
}

public static class OperatorOverloading
{
    public static void Main()
    {
        var a = new Fraction(5, 4);
        var b = new Fraction(1, 2);
        Console.WriteLine(-a);   // output: -5 / 4
        Console.WriteLine(a + b);  // output: 14 / 8
        Console.WriteLine(a - b);  // output: 6 / 8
        Console.WriteLine(a * b);  // output: 5 / 8
        Console.WriteLine(a / b);  // output: 10 / 4
    }
}

可以通过定义隐式转换intFraction来扩展前面的示例。 然后,重载运算符将支持这两种类型的参数。 也就是说,可以将一个整数添加到一个分数中,得到一个分数结果。

还可以使用 operator 关键字来定义自定义类型转换。 有关详细信息,请参阅用户定义转换运算符

可重载运算符

下表显示了可重载的运算符:

运营商 注释
+x-x!x~x++--truefalse 必须同时重载 truefalse 运算符。
x + y、、x - yx * yx / yx % y
x & y、、x | yx ^ y、、
x << yx >> yx >>> y
x == yx != yx < yx > yx <= yx >= y 必须按如下方式成对重载:==!=<><=>=
+=-=*=/=%=&=\|=^=<<=>>=>>>= 复合赋值运算符可以在 C# 14 及更高版本中重载。

复合赋值重载运算符必须遵循以下规则:

  • 它必须包含 public 修饰符。
  • 它不能包含 static 修饰符。
  • 返回类型必须是 void
  • 声明必须包含一个参数,该参数表示复合赋值右侧。

从C# 14版本开始,递增(++)和递减(--)运算符可以重载为实例成员。 实例运算符可以通过避免创建新实例来提高性能。 实例运算符必须遵循以下规则:

  • 它必须包含 public 修饰符。
  • 它不能包含 static 修饰符。
  • 返回类型必须是 void
  • 它不能声明任何参数,即使这些参数具有默认值。

不可重载运算符

下表显示了无法重载的运算符:

运营商 替代方案
x && yx || y 重载truefalse运算符以及&|运算符。 有关详细信息,请参阅 用户定义的条件逻辑运算符
a[i]a?[i] 定义 索引器
(T)x 定义由强制类型转换表达式执行的自定义类型转换。 有关详细信息,请参阅用户定义转换运算符
^x、、x = yx.yx?.yc ? t : fx ?? y、、 ??= y
x..yx->y=>f(x)asawaitcheckeduncheckeddefaultdelegateisnameofnew
sizeofstackallocswitchtypeofwith
没有。

在 C# 14 之前,复合运算符无法重载。 重载相应的二进制运算符会隐式重载相应的复合赋值运算符。

运算符重载解析

重要

本部分适用于 C# 14 及更高版本。 在 C# 14 之前,不允许用户定义的复合赋值运算符和实例递增和递减运算符。

如果在包含 x 的复合赋值表达式中,x «op»= y 被分类为变量,则对于 «op»,实例运算符优于任何静态运算符。 如果未为变量类型«op»=声明重x载运算符或x未分类为变量,则使用静态运算符。

对于后缀运算符 ++,如果未 x 分类为变量 或使用 表达式 x++ ,则忽略该实例 operator++ 。 否则,实例operator ++优先。 例如,

x++; // Instance operator++ preferred.
y = x++; // instance operator++ isn't considered.

此规则的目的是,在 y 被递增x,应将值赋给 。 编译器无法确定引用类型中用户定义实现的具体实现方式。

对于前缀运算符 ++,如果 x 分类为变量, ++x则实例运算符优先于静态一元运算符。

C# 语言规范

有关更多信息,请参阅 C# 语言规范的以下部分:

另请参阅