这几天,在网上搜索了一些关于判断对象相等的资料,很多,很杂,东云云,西云云,甚至连CSDN,策了半天,都没策出个名堂出来。
实践出真理,于是,我自己写了一些简单的,但有共性的东西,来探个究竟。
例一,目标是对值类型的判断。
(先暂时不考虑用户结构(struct)的判断,因为用户结构需要重写Equals方法,而重写Equals方法是头疼的,一般用户结构都不会重写Equals方法)
int str = 1;
int str2 = 1;
Console.WriteLine(str == str2);//true
Console.WriteLine(str.Equals(str2));//true Console.WriteLine(Object.Equals(str, str2));//true Console.WriteLine(Object.ReferenceEquals(str, str2));//false Console.WriteLine("------------");
int btr = 1;
int btr2 = 2;
Console.WriteLine(btr == btr2);//false
Console.WriteLine(btr.Equals(btr2));//false
Console.WriteLine(Object.Equals(btr, btr2));//false
Console.WriteLine(Object.ReferenceEquals(btr, btr2));//false
Console.WriteLine("------------");
输出结果;
从输出结果中,可以看到只有Object.ReferenceEquals(Object obj, Object obj)方法,不管值类型的字段是否相等,结果都输出为False,所以可以肯定的是;
一;Object.ReferenceEquals(Object obj, Object obj)方法永远输出False,不管值类型的字段是否相等。(因为值类型经过boxing之后,变成引用类型对象;而此时,值类型的字段已经不是保存的a或b,而是类对象的指针,即C++的指针,而这两个指针不相等)
二;“==”,”Int32.Equals(Object obj)”, ”Object.Equals(Object obj,Object obj)”,这三个方法,如果值类型的字段相等,那么都输出True;反之,输出False。
三;总结,对于值类型而言,比较对象是否相等,建议使用“==“,而不使用Equals方法,因为使用Equals方法需要boxing,而boxing是很耗内存和CPU的,完全是画蛇添足。
四,你如果怀疑Int32不能代表全部的值类型,可以试试其他类型,byte,float等,哈哈!
例二,目标是对引用类型的判断。
(暂不考虑System.String类型,因为String有他的特性)
情景一
//如果sb=”a”,sb2=”a”
StringBuilder sb = new StringBuilder("a");
StringBuilder sb2 = new StringBuilder("a");
Object sb3 = sb2;
Console.WriteLine(sb == sb2);//false Console.WriteLine(sb.Equals(sb2));//true
Console.WriteLine(Object.Equals(sb, sb2));//false Console.WriteLine(Object.Equals(sb2, sb3));//true Console.WriteLine(Object.ReferenceEquals(sb, sb2));//false Console.WriteLine(Object.ReferenceEquals(sb2, sb3));//true
Console.WriteLine(sb2 == sb3);// true
Console.WriteLine(sb2.Equals(sb3));//true
Console.WriteLine(Object.Equals(sb2, sb3));// true Console.WriteLine("------------");
情景二
//如果bsb =”a”, bsb2 =”b”
StringBuilder bsb = new StringBuilder("a");
StringBuilder bsb2 = new StringBuilder("b");
Object bsb3 = bsb2;
Console.WriteLine(bsb == bsb2);//false
Console.WriteLine(bsb.Equals(bsb2));//false Console.WriteLine(Object.Equals(bsb, bsb2));//false Console.WriteLine(Object.Equals(bsb2, bsb3));//true Console.WriteLine(Object.ReferenceEquals(bsb, bsb2));//false Console.WriteLine(Object.ReferenceEquals(bsb2, bsb3));//true
Console.WriteLine(bsb2 == bsb3);//true Console.WriteLine(bsb2.Equals(bsb3));//true Console.WriteLine(Object.Equals(bsb2, bsb3));//true
Console.WriteLine("------------");
输出结果
从输出结果可以看到倒数四个项输出都为True,为什么呢?因为Object sb3 = sb2和Object bsb3 = bsb2中的sb2和bsb2都是把自己的指针传递给目标sb3和sb2,都指向堆中的同一块内存。
再看,倒数第五项,都输出为False,因为Object.ReferenceEquals(Object obj,Object obj)方法比较的是对象的引用,即同一性。
再看倒数第六项和第七项,都输出为True和False,因为Object.Equals(Object ojb, Object obj),比较的也是对象的引用。
再看倒数第九项,都输出为False,因为”==”操作对引用类型而言,比较的是引用。
最后,来看看倒数第八项,sb.Equals(sb2)和bsb.Equals(bsb2),为什么我要放在最后说呢?因为这个方法比较特殊,他是具体类型的方法,(不同于System.Object.Equals(Object obj)方法)。用反射的方法,可以很容易的看到sb.Equals(sb2)的实现,如下;
public bool Equals(StringBuilder sb)
{
if ((sb != null) && ((this.Capacity == sb.Capacity) && (this.MaxCapacity == sb.MaxCapacity)))
{
return this.m_StringValue.Equals((string) sb.m_StringValue);
}
return false;
}
再看看祖宗的System.Object.Equals(Ojbect obj)是怎样实现的,如下;
public virtual bool Equals(object obj)
{
return InternalEquals(this, obj);
}
奇怪?InternalEquals(this, obj)的实行是啥呢?去看看;
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool InternalEquals(object objA, object objB);
而InternalEquals方法根本没有实现
可以看到每个具体类型的Equals()方法,都可能有不同的实现;所以,在判断引用类型是否相等的时候,要避免使用他们自己的Equals()方法,因为可能实现都不同,也有可能他娘的根本没实现,所以建议使用祖宗System.Object.ReferenceEquals(Object, Object)方法。
如果不信可以把StringBuilder bsb = new StringBuilder("a")改成Object bsb = new Object();bsb.Equals(…)输出结果不同。