对象判等

发布时间 2023-05-26 18:10:43作者: euv

实现引用类型的判等


步骤一:重写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,也就是运算符的宿主类型。在编译时,==和!=根据它的操作数类型确定调用哪一个方法。