C# - 运算符重载
C# - 运算符重载
Section titled “C# - 运算符重载”运算符重载允许你为 C# 的内置运算符定义自定义实现,以便与用户定义的类型(如类或结构体)一起使用。这可以使你的代码更加直观和可读,允许你编写 vector1 + vector2 而不是 vector1.Add(vector2)。
重载运算符被实现为 public static 方法,其名称特殊:由 operator 关键字后跟运算符符号构成。它们接受一个(一元运算符)或两个(二元运算符)参数并返回一个结果。
Box 类示例的现代化
Section titled “Box 类示例的现代化”让我们定义一个 Box 类。在现代 C# 中,我们使用属性进行数据访问,使用构造函数进行初始化,这比老式的 Set... 方法要简洁得多。
public class Box{ // 自动实现的尺寸属性 public double Length { get; private set; } public double Breadth { get; private set; } public double Height { get; private set; }
// 用于体积的只读计算属性 public double Volume => Length * Breadth * Height;
// 便于初始化的构造函数 public Box(double length, double breadth, double height) { Length = length; Breadth = breadth; Height = height; }
// 重载 + 运算符以添加两个 Box 对象 public static Box operator +(Box b1, Box b2) { // 创建一个具有组合尺寸的新盒子 return new Box(b1.Length + b2.Length, b1.Breadth + b2.Breadth, b1.Height + b2.Height); }
// 重写 ToString() 方法以获得清晰的字符串表示 public override string ToString() { return $"Box (L: {Length}, B: {Breadth}, H: {Height})"; }}使用重载运算符
Section titled “使用重载运算符”using System;
// 创建两个 Box 实例var box1 = new Box(6.0, 7.0, 5.0);var box2 = new Box(12.0, 13.0, 10.0);
Console.WriteLine($"Volume of Box1: {box1.Volume}");Console.WriteLine($"Volume of Box2: {box2.Volume}");
// 使用重载的 '+' 运算符添加两个盒子Box box3 = box1 + box2;
Console.WriteLine($"Details of Box3: {box3}");Console.WriteLine($"Volume of Box3: {box3.Volume}");
// 输出:// Box1 的体积: 210// Box2 的体积: 1560// Box3 详情: Box (L: 18, B: 20, H: 15)// Box3 的体积: 5400可重载与不可重载的运算符
Section titled “可重载与不可重载的运算符”| 类别 | 运算符 | 可重载? |
|---|---|---|
| 一元 | +, -, !, ~, ++, --, true, false | 是 |
| 二元 | +, -, *, /, %, &, |, ^, <<, >> | 是 |
| 比较 | ==, !=, <, >, <=, >= | 是(必须成对重载) |
| 条件逻辑 | &&, || | 否(但它们使用 & 和 | 进行短路求值) |
| 赋值 | +=, -=, *= etc. | 否(如果二元运算符被重载,则隐式使用) |
| 成员/其他 | ., [], (), ?:, new, is, as, typeof | 否([] 通过索引器定义,而非运算符重载) |
重载比较运算符的最佳实践
Section titled “重载比较运算符的最佳实践”当你重载相等运算符(==、!=)时,你必须遵循某些规则,以确保你的类型在 .NET 生态系统的其余部分中表现可预测。
- 成对重载:如果你重载
==,则也必须重载!=。同样适用于<和>,以及<=和>=。 - 重写
Equals()和GetHashCode():这一点至关重要。许多 .NET 组件,尤其是Dictionary和HashSet等集合,都依赖于Equals()和GetHashCode(),而不是==。你的==运算符和Equals方法应该具有一致的逻辑。 - 处理空值:你的
==和!=运算符必须正确处理一个或两个操作数为null的情况。
示例:重载 Vector2D 结构体上的相等性
Section titled “示例:重载 Vector2D 结构体上的相等性”struct(结构体)是此操作的良好候选者,因为它们是值类型。
public readonly struct Vector2D : IEquatable<Vector2D>{ public double X { get; } public double Y { get; }
public Vector2D(double x, double y) { X = x; Y = y; }
// 1. 重载 == public static bool operator ==(Vector2D v1, Vector2D v2) { return v1.X == v2.X && v1.Y == v2.Y; }
// 2. 重载 != (与 == 成对) public static bool operator !=(Vector2D v1, Vector2D v2) { return !(v1 == v2); }
// 3. 重写 Equals(object) 以实现通用相等性 public override bool Equals(object? obj) { return obj is Vector2D other && this == other; }
// 4. 实现 IEquatable<T> 以实现类型安全相等性 public bool Equals(Vector2D other) { return this == other; }
// 5. 只要重写 Equals,就必须重写 GetHashCode public override int GetHashCode() { return HashCode.Combine(X, Y); }}
// --- 用法 ---var vec1 = new Vector2D(3, 4);var vec2 = new Vector2D(3, 4);var vec3 = new Vector2D(5, 6);
Console.WriteLine($"vec1 == vec2: {vec1 == vec2}"); // TrueConsole.WriteLine($"vec1 != vec3: {vec1 != vec3}"); // TrueConsole.WriteLine($"vec1.Equals(vec2): {vec1.Equals(vec2)}"); // True指南: 仅当运算符的含义清晰直观时才重载它们,例如数学类型(向量、复数)或数据结构。例如,在 Person 类上重载 + 运算符将是模糊且令人困惑的。