如何使用==操作符,Equals方法,ReferenceEquals方法,IEquatable接口比较2个对象

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

“世界上不会有两片完全相同的树叶”,这句话适用于现实世界。而在软件世界中,这句话变成了"世界上必须有两片完全相同的树叶",否则,很多事情无以为继。

当比较2个对象是否相等时,通常情况下:==操作符用来比较值类型,比较的是值;实例方法Equals和静态方法Object.ReferenceEquals比较引用类型,比较的是对象的地址。

在实际项目中,当比较2个引用类型对象时,我们的需求变为:通过依次比较2个对象的所有属性来判断是否相等。这时候,IEquatable接口就有了展示自己的机会。本篇主要包括:

使用==操作符比较值类型是否相等实例方法Equals比较引用类型地址是否相等实现IEquatable接口重写实例方法Equals()

使用==操作符比较值类型是否相等

classPRogram

{

staticvoidMain(string[] args)

{

ComplexNumber a =newComplexNumber(){Real = 4.5D, Imaginary = 8.4D};

ComplexNumber b =newComplexNumber() { Real = 4.5D, Imaginary = 8.4D };

Console.WriteLine("{0} 是否等于{1}:{2}",a, b, CompareTwoComplexNumbers(a, b));

Console.ReadKey();

}

staticboolCompareTwoComplexNumbers(ComplexNumber a, ComplexNumber b)

{

return((a.Real == b.Real) && (a.Imaginary == b.Imaginary));

}

}

publicclassComplexNumber

{

publicdoubleReal {get;set; }

publicdoubleImaginary {get;set; }

publicoverridestringToString()

{

returnString.Format("{0}{1}{2}i",

Real,

Imaginary >= 0 ? "+" : "-",

Math.Abs(Imaginary));

}

}

以上,比较诸如int,double,DateTime,struct等值类型的时候,==操作符当然是不二之选。

实例方法Equals比较引用类型地址是否相等

classProgram

{

staticvoidMain(string[] args)

{

Guy darren1 =newGuy("Darren", 37, 100);

Guy darren2 = darren1;

Console.WriteLine(Object.ReferenceEquals(darren1,darren2));

Console.WriteLine(darren1.Equals(darren2));

Console.WriteLine(Object.ReferenceEquals(null,null));

darren2 =newGuy("Darren", 37, 100);

Console.WriteLine(Object.ReferenceEquals(darren1, darren2));

Console.WriteLine(darren1.Equals(darren2));

Console.ReadKey();

}

}

publicclassGuy

{

privatereadonlystringname;

publicstringName{get{returnname; }}

privatereadonlyintage;

publicintAge{get{returnage; }}

publicintCash {get;privateset; }

publicGuy(stringname,intage,intcash)

{

this.name = name;

this.age = age;

Cash = cash;

}

publicoverridestringToString()

{

returnString.Format("{0} 今年 {1} 岁了,身价{2}", Name, Age, Cash);

}

}

以上,实例方法Equals()和静态方法Object.ReferenceEquals()适用于比较引用类型,而且比较的是对象的地址。

可是,如果我们想使用Equals()方法比较引用类型对象的各个属性,怎么办呢?

实现IEquatable接口重写实例方法Equals()

写一个派生于Guy的类EquatableGuy,并且实现IEquatable<Guy>接口,重写IEquatable<Guy>接口的Equals(Guy other)方法。

classProgram

{

staticvoidMain(string[] args)

{

Guy darren1 =newEquatableGuy("Darren", 37, 100);

Guy darren2 =newEquatableGuy("Darren", 37, 100);

Console.WriteLine(Object.ReferenceEquals(darren1, darren2));//False

Console.WriteLine(darren1.Equals(darren2));//True

Console.ReadKey();

}

}

publicclassGuy

{

privatereadonlystringname;

publicstringName{get{returnname; }}

privatereadonlyintage;

publicintAge{get{returnage; }}

publicintCash {get;privateset; }

publicGuy(stringname,intage,intcash)

{

this.name = name;

this.age = age;

Cash = cash;

}

publicoverridestringToString()

{

returnString.Format("{0} 今年 {1} 岁了,身价{2}", Name, Age, Cash);

}

}

publicclassEquatableGuy : Guy, IEquatable<Guy>

{

publicEquatableGuy(stringname,intage,intcash) :base(name, age, cash){}

publicboolEquals(Guy other)

{

if(ReferenceEquals(null, other))returnfalse;

if(ReferenceEquals(this, other))returntrue;

returnEquals(other.Name, Name) && other.Age == Age && other.Cash == Cash;

}

publicoverrideboolEquals(objectobj)

{

if(!(objisGuy))returnfalse;

returnEquals((Guy) obj);

}

publicoverrideintGetHashCode()

{

constintprime = 397;

intresult = Age;

result = (result * prime) ^ (Name !=null? Name.GetHashCode() : 0);

result = (result * prime) ^ Cash;

returnresult;

}

}

以上,值得注意的是:

当实现IEquatable<Guy>接口时,一定要重写Object基类的Equals方法,然后在Object基类的Equals方法内部调用我们自定义的IEquatable<Guy>接口方法。另外还必须重写Object基类的GetHashCode方法。这时MSDN规定的,在这里。

而且,使用IEquatable<Guy>泛型接口还有一个好处是避免装箱和拆箱,因为在JIT编译时才替代占位符。而如果我们通过重写Object基类方法Equals实现自定义比较的话,难免会出现装箱和拆箱,影响比较性能。

如果我们项使用==操作符比较引用对象是否相等呢?我们可以通过重写操作符来实现。

我们再写一个EquatableGuy的子类EquatableGuyWithOverload,并且重写==操作符。

classProgram

{

staticvoidMain(string[] args)

{

var darren1 =newEquatableGuyWithOverload("Darren", 37, 100);

var darren2 =newEquatableGuyWithOverload("Darren", 37, 100);

Console.WriteLine(Object.ReferenceEquals(darren1, darren2));//False

Console.WriteLine(darren1 == darren2);//True

Console.ReadKey();

}

}

publicclassEquatableGuyWithOverload : EquatableGuy

{

publicEquatableGuyWithOverload(stringname,intage,intcash) :base(name, age, cash){}

publicstaticboolOperator==(EquatableGuyWithOverload left, EquatableGuyWithOverload right)

{

if(Object.ReferenceEquals(left,null))returnfalse;

elsereturnleft.Equals(right);

}

publicstaticbooloperator!=(EquatableGuyWithOverload left, EquatableGuyWithOverload right)

{

return!(left == right);

}

publicoverrideboolEquals(objectobj)

{

returnbase.Equals(obj);

}

publicoverrideintGetHashCode()

{

returnbase.GetHashCode();

}

}

以上,子类EquatableGuyWithOverload重写了==操作符,由于其父类EquatableGuy已经重写了基类Object的Equals方法,所以在这里可以直接调用。

总结:通常情况下,使用==操作符比较值类型对象;使用实例方法Equals或静态方法Object.ReferenceEquals比较引用类型对象地址;如果想自定义比较逻辑,可以考虑实现IEquatable<>泛型接口,避免装箱、拆箱。

参考资料:

防止装箱落实到底,只做一半也是失败

Understanding C#: Equality, IEquatable, and Equals()

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航