更新:2007 年 11 月
C# 中有两种不同的相等:引用相等和值相等。值相等是大家普遍理解的意义上的相等:它意味着两个对象包含相同的值。例如,两个值为 2 的整数具有值相等性。引用相等意味着要比较的不是两个对象,而是两个对象引用,且两者引用的是同一个对象。这可以通过简单的赋值来实现,如下面的示例所示:
C# | 复制代码 |
---|---|
System.Object a = new System.Object(); System.Object b = a; System.Object.ReferenceEquals(a, b); //returns true |
在上面的代码中,只存在一个对象,但存在对该对象的多个引用:a 和 b。由于它们引用的是同一个对象,因此具有引用相等性。如果两个对象具有引用相等性,则它们也具有值相等性,但是值相等性不能保证引用相等性。
若要检查引用相等性,应使用
重写 Equals
由于
Equals 的新实现应该遵循
x.Equals(x) 返回 true。
x.Equals(y) 与 y.Equals(x) 返回相同的值。
如果 (x.Equals(y) && y.Equals(z)) 返回 true,则 x.Equals(z) 返回 true。
只要不修改 x 和 y 所引用的对象,x.Equals(y) 的后续调用就返回相同的值。
x.Equals (null) 返回 false(仅非空值类型。有关更多信息,请参见可空类型(C# 编程指南)。)
Equals 的新实现不应该引发异常。建议重写 Equals 的任何类同时也重写
C# | 复制代码 |
---|---|
class TwoDPoint : System.Object { public readonly int x, y; public TwoDPoint(int x, int y) //constructor { this.x = x; this.y = y; } public override bool Equals(System.Object obj) { // If parameter is null return false. if (obj == null) { return false; } // If parameter cannot be cast to Point return false. TwoDPoint p = obj as TwoDPoint; if ((System.Object)p == null) { return false; } // Return true if the fields match: return (x == p.x) && (y == p.y); } public bool Equals(TwoDPoint p) { // If parameter is null return false: if ((object)p == null) { return false; } // Return true if the fields match: return (x == p.x) && (y == p.y); } public override int GetHashCode() { return x ^ y; } } |
可调用基类的 Equals 的任何派生类在完成其比较之前都应该这样做。在下面的示例中,Equals 调用基类 Equals,后者将检查空参数并将参数的类型与派生类的类型做比较。这样就把检查派生类中声明的新数据字段的任务留给了派生类中的 Equals 实现:
C# | 复制代码 |
---|---|
class ThreeDPoint : TwoDPoint { public readonly int z; public ThreeDPoint(int x, int y, int z) : base(x, y) { this.z = z; } public override bool Equals(System.Object obj) { // If parameter cannot be cast to ThreeDPoint return false: ThreeDPoint p = obj as ThreeDPoint; if ((object)p == null) { return false; } // Return true if the fields match: return base.Equals(obj) && z == p.z; } public bool Equals(ThreeDPoint p) { // Return true if the fields match: return base.Equals((TwoDPoint)p) && z == p.z; } public override int GetHashCode() { return base.GetHashCode() ^ z; } } |
重写运算符 ==
默认情况下,运算符 == 通过判断两个引用是否指示同一对象来测试引用是否相等。因此引用类型不需要实现运算符 == 就能获得此功能。当类型不可变(即实例中包含的数据不可更改)时,通过重载运算符 == 来比较值是否相等而不是比较引用是否相等可能会很有用,因为作为不可变的对象,只要其值相同,就可以将其视为相同。建议不要在非不可变类型中重写运算符 ==。
重载的运算符 == 实现不应引发异常。重载运算符 == 的任何类型还应重载运算符 !=。例如:
C# | 复制代码 |
---|---|
//add this code to class ThreeDPoint as defined previously // public static bool operator ==(ThreeDPoint a, ThreeDPoint b) { // If both are null, or both are same instance, return true. if (System.Object.ReferenceEquals(a, b)) { return true; } // If one is null, but not both, return false. if (((object)a == null) || ((object)b == null)) { return false; } // Return true if the fields match: return a.x == b.x && a.y == b.y && a.z == b.z; } public static bool operator !=(ThreeDPoint a, ThreeDPoint b) { return !(a == b); } |
说明: |
---|
运算符 == 的重载中的常见错误是使用 (a == b)、(a == null) 或 (b == null) 来检查引用相等性。这会调用重载的运算符 ==,从而导致无限循环。应使用 |