首页 > 代码库 > 在对象比较中,对象相等和对象一致分别指的是什么?
在对象比较中,对象相等和对象一致分别指的是什么?
对象数据存内存堆里,对象一致即内存堆相同,对象相等为哈希表中键同值可能不同
简单的讲:相等性(quality)就是两个对象,它们的值相等。同一性(identity)就是指引用的是否为同一个对象。 下面是我的读书笔记: C# 中有两种不同的相等:引用相等和值相等。值相等是大家普遍理解的意义上的相等:它意味着两个对象包含相同的值。例如,两个值为 2 的整数具有值相等性。
引用相等意味着要比较的不是两个对象,而是两个对象引用,这两个对象引用所引用的是同一个对象。我们这里把值相等叫做对象的“相等性(equality)”,把引用相等叫做对象的同一性(identity)。
我们都知道在System.Object类型中提供了一个名为Equals的虚方法,它的作用是在两个对象相等的情况下返回true,不相等时返回false。等等,这里的说的“相等”是哪个?是“相等性(equality)”还是“同一性(identity)”。
好吧,来看一下代码吧,System的Object方法是像下面这样实现的:
public class Object { public virtual Boolean Equals(Object obj)
{
// 如果两个引用指向的是同一个对象,那么它们肯定相等
if (this == obj) return true;
// 假定对象不相等
return false;
}
}
表明上看它好像是实现的很合理:
假如this和Obj引用同一对象,自然就是true了,因为Equals知道一个对象肯定等于它自身。
然而,如果this和Obj引用不同的对象哪?,Equals就不能肯定对象是否包含相同的值,
所以总结一句话就是:Object的Equals方法实现的只是“同一性(identity)”,而不是“相等性(equality)”。
多么的令人遗憾啊~!Object的Equals的默认实现并不合理,既然不合理我们就来重写它吧,先来看看如何在内部正确实现一个Equals方法(我把它概括为四个字——空、型、值、基):
如果obj参数为null,就返回false;
如果this和obj参数引用不同的类型对象,返回false;
针对类型定义的每个实例字段,将this对象的值和obj对象的值进行对比,任何字段不相等,就返回false;
调用基类的Equals方法,以便比较它定义的任何字段。如果基类的Equals方法返回false,就返回false;否则返回true;
再来看一下Object的Equals方法的实现代码:
public class Object
{
public virtual Boolean Equals(Object obj)
{
if (obj == null)
return false;
if (this.GetType() != obj.GetType())
return false;
// 如果对象属于相同的类型,那么在它们的多有字段都匹配的前提下返回true
// 由于System.Object没有定义任何字段,所以字段是匹配的
return true;
}
}
其实,这里要说一下,Microsoft并没有这样去实现他的代码,而是要比这个复杂的多的多。
那么Equals方法可以在子类中重写,那么就不可以用Equals方法来测试同一性(identity)了。怎么办啊?
Microsoft在Object中提供了一个静态方法ReferenceEquals,其原型如下:
public class Object
{
public static Boolean ReferenceEquals(Object objA, object objB)
{
return (objA == objB);
}
}
注意了啊,如果想要检查同一性(identity),那么务必调用ReferenceEquals,而不应该使用C#的==操作符(除非事先把它们转化为Object类型),
原因是其中某个操作数的类型可能重载了==操作符,为其赋予了其它语义。
另外,System.ValueType重写了Object的Equals方法。并进行了正确的实现来执行相等性(equality)检查,而不是同一性(identity)检查。
在内部,ValueType的Equals方法是像这样实现的:
如果obj参数为null,返回false; 如果this和obj引用的不同类型的对象,返回false;
针对类型定义的每个实例字段,都将this对象的值和obj对象中的值进行比较。
如果有字段不相等,就返回false; 返回true;
ValueType的Equals的方法不会调用Object的Equals方法。
顺便说一下,ValueType的Equals方法是通过反射技术来完成的。由于CLR反射机制较慢,所以在定义自己的值类型时,应该重写Equals方法,并提供自己的实现,以便提高性能。当然在自己的实现中不要调用base.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。
对象们都住在不同的房间里,每个房间只能住一个对象.对象们都被锁在房间里,永远没有办法搬家(至少从我们讨论的角度来说,这个说法是正确的).所以如果你知道了一个对象的
房间号,就能找到对应的对象. 现在假如我们有两张名片,上面如果写着相同的房间号,我们就可以断定,这两张名片是同一个对象分发出来的,这就是同一性,也就是你所说的一致. 假如1
号房里住着一个值为1的整数对象, 2号房里住着另一个值为2的整数对象,3号房里住着另另一个值为1的整数对象.我们又有它们各自的一张名片, 那么,第一个名片和第三个对应的对
象的值是相等的,但是它们不是同一个对象,用你的词来说,也就是说它们"相等",但不"一致". 如果上面说得太清楚了,那么这里让你再困扰一会吧,哈哈: 这里的名片就是引用(i, count
之类的变量/字段等的名称); 房间所在的大楼就是内存,房间号就是内存地址. 对象就是内存里保存的数据.
对象们都住在不同的房间里,每个房间只能住一个对象.对象们都被锁在房间里,永远没有办法搬家(至少从我们讨论的角度来说,这个说法是正确的).所以如果你知道了一个对象的房间号,就能找到对应的对象.
现在假如我们有两张名片,上面如果写着相同的房间号,我们就可以断定,这两张名片是同一个对象分发出来的,这就是同一性,也就是你所说的一致.
假如1号房里住着一个值为1的整数对象, 2号房里住着另一个值为2的整数对象,3号房里住着另另一个值为1的整数对象.我们又有它们各自的一张名片, 那么,第一个名片和第三个对应的对象的值是相等的,但是它们不是同一个对象,用你的词来说,也就是说它们相等,但不一致.
如果上面说得太清楚了,那么这里让你再困扰一会吧,哈哈:
这里的名片就是引用(i, count之类的变量/字段等的名称);
房间所在的大楼就是内存,房间号就是内存地址.对象就是内存里保存的数据.
在对象比较中,对象相等和对象一致分别指的是什么?