实现引用类型的判等
| 步骤一:重写Equals() |
public class RefPoint
{
public int X { get; set; }
public int Y { get; set; }
public override bool Equals(object obj)
{
// 如果指向堆中同一个实例,必然相等
if (ReferenceEquals(this, obj))
{
return true;
}
// 如果obj是NULL或不是RefPoint类型或不是RefPoint的派生类,认为不相等
var point = obj as RefPoint;
if (point == null)
{
return false;
}
// obj必须是RefPoint类型,否则认为不相等
if (obj.GetType() != GetType())
{
return false;
}
// 比较此类型定义的新增字段
if (X == point.X && Y == point.Y)
{
return true;
// return base.Equals(point); // RefPoint直接继承于Object,所以没必要比较从基类继承来的字段
}
return false;
}
}
这一步解决了同一性和相等性问题,类型能够以自定义逻辑进行判等。
解释下base.Equals(point)
使用base可以调用由于重写被隐藏的基类方法,在Equals()中调用base.Equals()有点递归的感觉,因为base.equals()同样会执行自己的逻辑,开头仍旧是同一性判断,类型判断,最后是调用base.base.Equals()。
值得说明的是,base.Equals()代码逻辑中的this和GetType()都会基于派生类解释,所以都能正常通过,后面的字段判等,其实就是利用基类中现成的方法,去判断派生类中从基类继承来的那部分字段,才是真正的目的。
| 第二步:重载==和!= |
public class RefPoint
{
public int X { get; set; }
public int Y { get; set; }
public override bool Equals(object obj)
{
// 如果指向堆中同一个实例,必然相等
if (ReferenceEquals(this, obj))
{
return true;
}
// 如果obj是NULL或不是RefPoint类型或不是RefPoint的派生类,认为不相等
var point = obj as RefPoint;
if (point == null)
{
return false;
}
// obj必须是RefPoint类型,否则认为不相等
if (obj.GetType() != GetType())
{
return false;
}
// 比较此类型定义的新增字段
if (X == point.X && Y == point.Y)
{
return true;
// return base.Equals(point); // 直接继承Object,所以没必要比较从基类继承来的字段
}
return false;
}
// 第二个参数的类型个人建议是object而非RefPoint,这样才能和Equals完全等价,即可比较任意对象
public static bool operator ==(RefPoint p1, object p2)
{
return Equals(p1, p2);
}
// 第二个参数的类型个人建议是object而非RefPoint,这样才能和Equals完全等价,即可比较任意对象
public static bool operator !=(RefPoint p1, object p2)
{
return !Equals(p1, p2);
}
}
Object-Equals(object obj),==,!= 的语义相同,完全等价,所以应该遵守规范:若重写了Object-Equals(object obj),务必也重载运算符 == 和 !=。
重载==和!=的方法是静态方法,且至少有一个参数的类型是RefPoint,也就是运算符的宿主类型。在编译时,==和!=根据它的操作数类型确定调用哪一个方法。