运算符重载在 C# 中只能在
struct或
class内部用
public static方法实现,且必须使用
operator关键字声明;不能重载所有运算符,也不能改变运算符优先级或结合性。
哪些运算符可以重载
C# 允许重载的运算符有明确限制。可重载的包括:
一元运算符:+、
-、
!、
~、
++、
--、
true、
false二元运算符:
+、
-、
*、
/、
%、
&、
|、
^、
、<code>>>、
==、
!=、
、<code>>、
、<code>>=必须成对重载:
==和
!=、
和 <code>>、
和 <code>>=、
true和
false
不可重载的包括:
&&、
||、
?:、
=、
.、
?:、
->、
sizeof、
typeof、
new、
checked、
unchecked等。
重载 ==
和 !=
的正确写法
这是最容易出错的地方:重载
==后,编译器会强制要求同时重载
!=,并且还必须重写
Equals(object)和
GetHashCode(),否则会触发 CS0660 / CS0661 警告。
典型错误是只重载
==,或在
==中直接调用
object.ReferenceEquals但没处理
null参数。
public class Vector2
{
public double X { get; }
public double Y { get; }
<pre class='brush:php;toolbar:false;'>public Vector2(double x, double y) => (X, Y) = (x, y);
public static bool operator ==(Vector2 a, Vector2 b)
{
if (ReferenceEquals(a, b)) return true;
if (a is null || b is null) return false;
return a.X == b.X && a.Y == b.Y;
}
public static bool operator !=(Vector2 a, Vector2 b) => !(a == b);
public override bool Equals(object obj) => obj is Vector2 v && this == v;
public override int GetHashCode() => HashCode.Combine(X, Y);}
重载 +
运算符并支持混合类型
如果希望
Vector2 + double也能工作,不能只写
Vector2 + Vector2;需额外提供参数类型不同的重载版本。但要注意隐式转换可能引发歧义。 推荐显式提供
Vector2 + double和
double + Vector2两个版本(后者因交换律必要) 避免定义
implicit operator double(Vector2),否则
vector + 1.0可能被误解释为
(double)vector + 1.0返回新实例而非修改原对象(符合值语义直觉)
public static Vector2 operator +(Vector2 a, Vector2 b) =>
new Vector2(a.X + b.X, a.Y + b.Y);
<p>public static Vector2 operator +(Vector2 v, double s) =>
new Vector2(v.X + s, v.Y + s);</p><p>public static Vector2 operator +(double s, Vector2 v) =>
new Vector2(v.X + s, v.Y + s);为什么重载 ++
要返回新对象而不是 this
因为
++是一元后缀/前缀运算符,C# 要求其返回值参与表达式计算。若返回
this并修改内部状态,会导致与内置数值类型行为不一致(如
int的
++返回副本),也破坏不可变设计意图。
更关键的是:如果
++修改自身又返回
this,则
v1 = v2++;会让
v1和
v2引用同一对象,后续修改
v2会意外影响
v1—— 这在结构体中还会因装箱引发更隐蔽问题。
所以标准做法是:
前缀++:创建新实例,返回新实例 后缀
++:先保存当前状态,再创建新实例,返回保存的旧状态
public static Vector2 operator ++(Vector2 v) => new Vector2(v.X + 1, v.Y + 1);
<p>// 后缀版本需手动模拟(C# 不自动区分,靠方法签名)
public static Vector2 operator ++(ref Vector2 v)
{
var old = v;
v = new Vector2(v.X + 1, v.Y + 1);
return old;
}注意:C# 实际不支持
ref形参用于运算符重载(上述写法非法),真正合规的后缀
++必须返回旧值副本,且不能修改原对象 —— 所以多数场景下应避免重载
++,改用明确的
Increment()方法。
