C# 运算符重载

可重载的运算符、必须成对定义、以及隐式/显式转换。

Advanced
Key Points重载 `==` 必须同时重载 `!=`,并重写 `Equals` 和 `GetHashCode`。

基本用法

C#
class Vector
{
    public double X { get; }
    public double Y { get; }

    public Vector(double x, double y) { X = x; Y = y; }

    public static Vector operator +(Vector a, Vector b) => new(a.X + b.X, a.Y + b.Y);
    public static Vector operator -(Vector a, Vector b) => new(a.X - b.X, a.Y - b.Y);
    public static Vector operator *(Vector a, double k) => new(a.X * k, a.Y * k);

    // 一元
    public static Vector operator -(Vector a) => new(-a.X, -a.Y);

    public override string ToString() => $"({X},{Y})";
}

var a = new Vector(1, 2);
var b = new Vector(3, 4);

Console.WriteLine(a + b);
Console.WriteLine(a - b);
Console.WriteLine(a * 2);
Console.WriteLine(-a);
Output
(4,6)
(-2,-2)
(2,4)
(-1,-2)

比较运算符

C#
class Money : IEquatable<Money>
{
    public decimal Amount { get; }
    public string Currency { get; }

    public Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }

    // == 和 != 必须成对
    public static bool operator ==(Money? a, Money? b) => Equals(a, b);
    public static bool operator !=(Money? a, Money? b) => !Equals(a, b);

    public static bool operator >(Money a, Money b) => a.Amount > b.Amount;
    public static bool operator <(Money a, Money b) => a.Amount < b.Amount;

    // 重载 == 就必须重写 Equals 和 GetHashCode
    public bool Equals(Money? other) =>
        other is not null && Amount == other.Amount && Currency == other.Currency;

    public override bool Equals(object? obj) => Equals(obj as Money);

    public override int GetHashCode() => HashCode.Combine(Amount, Currency);

    public override string ToString() => $"{Amount} {Currency}";
}

var m1 = new Money(100, "CNY");
var m2 = new Money(100, "CNY");
var m3 = new Money(200, "CNY");

Console.WriteLine(m1 == m2);
Console.WriteLine(m1 != m3);
Console.WriteLine(m3 > m1);
Output
True
True
True
Overloading == without overriding Equals/GetHashCode triggers warnings (CS0660/CS0661) and makes Dictionary, HashSet and LINQ Distinct disagree. Always change all three together.

转换运算符

C#
class Celsius
{
    public double Value { get; }
    public Celsius(double v) => Value = v;

    // 隐式:不会丢信息
    public static implicit operator double(Celsius c) => c.Value;

    // 显式:可能丢信息,必须强转
    public static explicit operator int(Celsius c) => (int)c.Value;

    public override string ToString() => $"{Value}°C";
}

var c = new Celsius(36.6);

double d = c;              // 隐式,直接赋值
int i = (int)c;            // 显式,需要强转

Console.WriteLine(c);
Console.WriteLine(d);
Console.WriteLine(i);
Output
36.6°C
36.6
36

Use implicit when the conversion is always safe (a wrapper to its raw value) and explicit when precision can be lost. Overusing implicit hides conversions and makes debugging hard.

可重载的运算符

OverloadableNot overloadable
+ - * / %&& ||(需同时重载 true/false)
== != < > <= >=. ?. ::
! ~ ++ --= += 等赋值(由运算派生)
& | ^ << >>new typeof sizeof
true falseis as ?:
[](索引器)?? ??=

该不该重载

  • 该 —— 数学/物理类型(Vector、Matrix、Complex)、金额、日期区间
  • 该 —— 运算符的含义直观且无歧义
  • 不该 —— 业务实体(Order + Order 是什么意思?)
  • 不该 —— 有副作用(a + b 改了数据库就太可怕了)

The test is simple: overload when the mathematical intuition holds; otherwise use a named method. money1 + money2 reads naturally; order1 + order2 confuses — call it Merge.

Friends & Partners