C# 允許為使用者定義型別(類別、結構)重新定義內建運算子的行為,這稱為運算子多載。透過運算子多載,自訂型別能像內建型別一樣自然地進行數學運算、比較、邏輯判斷,大幅提升程式碼的可讀性與表達力。
運算子多載是特殊的 static 方法,命名規則為 operator <符號>:
public static Box operator +(Box left, Box right) {
return new Box {
Length = left.Length + right.Length,
Width = left.Width + right.Width,
Height = left.Height + right.Height
};
}
關鍵規則:
public static| 類別 | 可多載 | 不可多載 |
|---|---|---|
| 一元運算子 | + - ! ~ ++ -- true false | — |
| 二元運算子 | + - * / % | — |
| 比較運算子 | == != < > <= >= | — |
| 邏輯運算子 | — | && ||(但可透過 & | false true 間接支援) |
| 指派運算子 | — | = += -= *= /= %= |
| 其他 | — | [] () ?. ?? => new typeof sizeof is as |
注意:== 與 !=、< 與 >、<= 與 >= 必須成對多載,否則編譯器會發出警告。
public class Box {
public double Length { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public double Volume => Length * Width * Height;
// 加法:合併兩個箱子
public static Box operator +(Box a, Box b) => new Box {
Length = a.Length + b.Length,
Width = a.Width + b.Width,
Height = a.Height + b.Height
};
// 相等比較:必須成對實作 !=
public static bool operator ==(Box a, Box b) {
if (ReferenceEquals(a, b)) return true;
if (a is null || b is null) return false;
return a.Length == b.Length && a.Width == b.Width && a.Height == b.Height;
}
public static bool operator !=(Box a, Box b) => !(a == b);
// 大小比較:成對實作
public static bool operator <(Box a, Box b) => a.Volume < b.Volume;
public static bool operator >(Box a, Box b) => a.Volume > b.Volume;
public static bool operator <=(Box a, Box b) => a.Volume <= b.Volume;
public static bool operator >=(Box a, Box b) => a.Volume >= b.Volume;
// 覆寫 Equals/GetHashCode 配合 == 運算子
public override bool Equals(object obj) => this == (obj as Box);
public override int GetHashCode() => HashCode.Combine(Length, Width, Height);
public override string ToString() => $"({Length}, {Width}, {Height}) Vol={Volume}";
}
var box1 = new Box { Length = 6, Width = 7, Height = 5 }; // 體積 210
var box2 = new Box { Length = 12, Width = 13, Height = 10 }; // 體積 1560
var box3 = box1 + box2; // (18, 20, 15) 體積 5400
Console.WriteLine($"Box1: {box1}");
Console.WriteLine($"Box2: {box2}");
Console.WriteLine($"Box3 = Box1 + Box2: {box3}");
Console.WriteLine($"Box1 > Box2: {box1 > box2}"); // False
Console.WriteLine($"Box1 < Box2: {box1 < box2}"); // True
Console.WriteLine($"Box1 == Box3: {box1 == box3}"); // False
var box4 = box3;
Console.WriteLine($"Box3 == Box4: {box3 == box4}"); // True
輸出結果:
Box1: (6, 7, 5) Vol=210
Box2: (12, 13, 10) Vol=1560
Box3 = Box1 + Box2: (18, 20, 15) Vol=5400
Box1 > Box2: False
Box1 < Box2: True
Box1 == Box3: False
Box3 == Box4: True
雖非算術運算子,但 implicit / explicit 運算子同樣重要:
public struct Temperature {
public double Celsius { get; }
public Temperature(double c) => Celsius = c;
public static implicit operator Temperature(double c) => new Temperature(c);
public static explicit operator double(Temperature t) => t.Celsius;
public static implicit operator Temperature(Fahrenheit f) => new Temperature((f.Value - 32) * 5/9);
}
介面不能宣告運算子多載(C# 11 引入 static abstract 除外)。若需在泛型限制中使用運算子,請使用 System.Numerics 中的介面(如 INumber<T>)。
比較運算子應正確處理 null:
public static bool operator ==(Box a, Box b) {
if (ReferenceEquals(a, b)) return true;
if (a is null || b is null) return false;
return a.Length == b.Length && a.Width == b.Width && a.Height == b.Height;
}
+ 回傳新 Box),頻繁使用可能增加 GC 壓力。Add、Equals 等方法別名,供不支援運算子多載的語言使用。+ 應代表加法/合併,== 應代表值相等,避免違反直覺的多載。== 需配合 Equals/GetHashCode 覆寫。