C# 父类实现的 IEquatable<T> 接口,重写的相等性判断函数都能被子类继承

实验代码

Son s1 = new(), s2 = new();

Console.WriteLine("_ = s1 == s2");
_ = s1 == s2;
Console.WriteLine();

Console.WriteLine("_ = s1 != s2;");
_ = s1 != s2;
Console.WriteLine();

Console.WriteLine("_ = s1.Equals(s2);");
_ = s1.Equals(s2);
Console.WriteLine();

Console.WriteLine("_ = ((object)s1).Equals(s2);");
_ = ((object)s1).Equals(s2);
Console.WriteLine();

Console.WriteLine("s1 is IEquatable<Father>");
Console.WriteLine(s1 is IEquatable<Father>);
Console.WriteLine();

Console.WriteLine("s1.GetHashCode()");
Console.WriteLine(s1.GetHashCode());
Console.WriteLine();

public class Father : IEquatable<Father>
{
	public virtual int A { get; set; }
	public virtual int B { get; set; }

	public override bool Equals(object? obj)
	{
		Console.WriteLine("object Equals");
		if (obj is not null and Father)
		{
			Father f = (Father)obj;
			return f.A == A && f.B == B;
		}

		return false;
	}

	public override int GetHashCode()
	{
		Console.WriteLine("GetHashCode");
		return 0;
	}

	public static bool operator ==(Father left, Father right)
	{
		Console.WriteLine("比较==");
		return left.Equals(right);
	}

	public static bool operator !=(Father left, Father right)
	{
		Console.WriteLine("比较!=");
		return !(left == right);
	}

	public bool Equals(Father? other)
	{
		Console.WriteLine("IEquatable Equals");
		return ((object)this).Equals(other);
	}
}

public class Son : Father
{
	public override int A { get => base.A; set => base.A = value; }
}

运行结果

_ = s1 == s2
比较==
IEquatable Equals
object Equals

_ = s1 != s2;
比较!=
比较==
IEquatable Equals
object Equals

_ = s1.Equals(s2);
IEquatable Equals
object Equals

_ = ((object)s1).Equals(s2);
object Equals

s1 is IEquatable<Father>
True

s1.GetHashCode()
GetHashCode
0

在父类重写的 Equals 方法,继承的 IEquatable 接口,重写的 == ,!= 运算符,重写的 GetHashCode 方法在子类中仍然是表现为被重写的状态。

子类实现 IEquatable 接口

现在让子类也实现 IEquatable 接口

Son s1 = new(), s2 = new();

s1.Equals(s2);

public class Father : IEquatable<Father>
{
	public virtual int A { get; set; }
	public virtual int B { get; set; }

	public override bool Equals(object? obj)
	{
		Console.WriteLine("object Equals");
		if (obj is not null and Father)
		{
			Father f = (Father)obj;
			return f.A == A && f.B == B;
		}

		return false;
	}

	public override int GetHashCode()
	{
		Console.WriteLine("GetHashCode");
		return 0;
	}

	public static bool operator ==(Father left, Father right)
	{
		Console.WriteLine("比较==");
		return left.Equals(right);
	}

	public static bool operator !=(Father left, Father right)
	{
		Console.WriteLine("比较!=");
		return !(left == right);
	}

	public bool Equals(Father? other)
	{
		Console.WriteLine("IEquatable Equals");
		return ((object)this).Equals(other);
	}
}

public class Son : Father, IEquatable<Son>
{
	public bool Equals(Son? other)
	{
		Console.WriteLine("IEquatable<Son>.Equals");
		return ((object)this).Equals(other);
	}
}

运行结果

IEquatable<Son>.Equals
object Equals

Equals 方法的重载

此时代码提示如下
在这里插入图片描述
子类实现接口后,调用的默认就是子类实现的 Equals 方法。也可以用强制转换调用父类的 Equals 方法
在这里插入图片描述
这里属于 Equals 方法的不同类型的重载。通过 public override bool Equals(object? obj) 重写的 Equals 方法也是一种重载。这个重载是每一个 object 的子类都有的,不重写这个方法时比较的是两个对象的引用是否相等。

所有判断相等性的运算符、方法必须一起重写

上面那些判断相等性的方法、运算符必须要么一起重写,要么全部不重写。实现了 IEquatable 接口后也要重写全部。如果不全部重写,这些不同的相等性判断方法返回结果不一致会造成混乱。

对象放到哈希表的过程

对象在放到哈希表里首先是计算哈希值,然后尝试放到哈希表中的对应位置。如果该位置已经有一个对象了,再调用 Equals 方法判断是不是同一个对象,如果是,就不再放一次了。如果不是,则说明发生了哈希冲突,此时会将新对象用链表挂在哈希表该位置。

重写 Equals 方法后,如果将对象放在哈希表里,必须重写 GetHashCode 方法

重写相等性判断方法时,如果想让对象能够在哈希表里正常工作,必须也重写 GetHashCode 方法。GetHashCode 方法原本是根据引用值生成哈希码,原本的相等性运算也是根据引用值。这种情况可以正常工作。但是,如果重写了 Equals 方法,不重写 GetHashCode 方法,会导致明明相等的两个对象返回的哈希值不同(因为他们的引用不同),于是哈希表中的两个位置储存了两个相等的对象。

重写 GetHashCode 的注意事项

  1. 不要使用会变化的字段生成哈希码。因为字段变化后,生成的哈希码不同,此时再调用 GetHashCode 方法得到的哈希码不同,这会导致字段变化后无法在哈希表中找到该对象了。
  2. 两个对象使用 Equals 方法判断相等性返回 true 时,GetHashCode 方法必须能够返回相同的值。否则会导致两个相等的对象具有不同的哈希值,这会导致哈希表中有重复的元素。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值