如何使用==操作符,Equals方法,ReferenceEquals方法,IEquatable接口比较2个对象
“世界上不会有两片完全相同的树叶”,这句话适用于现实世界。而在软件世界中,这句话变成了"世界上必须有两片完全相同的树叶",否则,很多事情无以为继。
当比较2个对象是否相等时,通常情况下:==操作符用来比较值类型,比较的是值;实例方法Equals和静态方法Object.ReferenceEquals比较引用类型,比较的是对象的地址。
在实际项目中,当比较2个引用类型对象时,我们的需求变为:通过依次比较2个对象的所有属性来判断是否相等。这时候,IEquatable接口就有了展示自己的机会。本篇主要包括:
使用==操作符比较值类型是否相等
class Program{static void Main(string[] args){ComplexNumber a = new ComplexNumber(){Real = 4.5D, Imaginary = 8.4D};ComplexNumber b = new ComplexNumber() { Real = 4.5D, Imaginary = 8.4D };Console.WriteLine("{0} 是否等于{1}:{2}",a, b, CompareTwoComplexNumbers(a, b));Console.ReadKey();}static bool CompareTwoComplexNumbers(ComplexNumber a, ComplexNumber b){return ((a.Real == b.Real) && (a.Imaginary == b.Imaginary));}}public class ComplexNumber{public double Real { get; set; }public double Imaginary { get; set; }public override string ToString(){return String.Format("{0}{1}{2}i",Real,Imaginary >= 0 ? "+" : "-",Math.Abs(Imaginary));}}

以上,比较诸如int,double,DateTime,struct等值类型的时候,==操作符当然是不二之选。
实例方法Equals比较引用类型地址是否相等
class Program{static void Main(string[] args){Guy darren1 = new Guy("Darren", 37, 100);Guy darren2 = darren1;Console.WriteLine(Object.ReferenceEquals(darren1,darren2));Console.WriteLine(darren1.Equals(darren2));Console.WriteLine(Object.ReferenceEquals(null, null));darren2 = new Guy("Darren", 37, 100);Console.WriteLine(Object.ReferenceEquals(darren1, darren2));Console.WriteLine(darren1.Equals(darren2));Console.ReadKey();}}public class Guy{private readonly string name;public string Name{get { return name; }}private readonly int age;public int Age{get { return age; }}public int Cash { get; private set; }public Guy(string name, int age, int cash){this.name = name;this.age = age;Cash = cash;}public override string ToString(){return String.Format("{0} 今年 {1} 岁了,身价{2}", Name, Age, Cash);}}

以上,实例方法Equals()和静态方法Object.ReferenceEquals()适用于比较引用类型,而且比较的是对象的地址。
可是,如果我们想使用Equals()方法比较引用类型对象的各个属性,怎么办呢?
实现IEquatable接口重写实例方法Equals()
写一个派生于Guy的类EquatableGuy,并且实现IEquatable<Guy>接口,重写IEquatable<Guy>接口的Equals(Guy other)方法。
class Program{static void Main(string[] args){Guy darren1 = new EquatableGuy("Darren", 37, 100);Guy darren2 = new EquatableGuy("Darren", 37, 100);Console.WriteLine(Object.ReferenceEquals(darren1, darren2)); //FalseConsole.WriteLine(darren1.Equals(darren2)); //TrueConsole.ReadKey();}}public class Guy{private readonly string name;public string Name{get { return name; }}private readonly int age;public int Age{get { return age; }}public int Cash { get; private set; }public Guy(string name, int age, int cash){this.name = name;this.age = age;Cash = cash;}public override string ToString(){return String.Format("{0} 今年 {1} 岁了,身价{2}", Name, Age, Cash);}}public class EquatableGuy : Guy, IEquatable<Guy>{public EquatableGuy(string name, int age, int cash) : base(name, age, cash){}public bool Equals(Guy other){if (ReferenceEquals(null, other)) return false;if (ReferenceEquals(this, other)) return true;return Equals(other.Name, Name) && other.Age == Age && other.Cash == Cash;}public override bool Equals(object obj){if (!(obj is Guy)) return false;return Equals((Guy) obj);}public override int GetHashCode(){const int prime = 397;int result = Age;result = (result * prime) ^ (Name != null ? Name.GetHashCode() : 0);result = (result * prime) ^ Cash;return result;}}

以上,值得注意的是:
当实现IEquatable<Guy>接口时,一定要重写Object基类的Equals方法,然后在Object基类的Equals方法内部调用我们自定义的IEquatable<Guy>接口方法。另外还必须重写Object基类的GetHashCode方法。这时MSDN规定的,在这里。
而且,使用IEquatable<Guy>泛型接口还有一个好处是避免装箱和拆箱,因为在JIT编译时才替代占位符。而如果我们通过重写Object基类方法Equals实现自定义比较的话,难免会出现装箱和拆箱,影响比较性能。
如果我们项使用==操作符比较引用对象是否相等呢?我们可以通过重写操作符来实现。
我们再写一个EquatableGuy的子类EquatableGuyWithOverload,并且重写==操作符。
class Program{static void Main(string[] args){var darren1 = new EquatableGuyWithOverload("Darren", 37, 100);var darren2 = new EquatableGuyWithOverload("Darren", 37, 100);Console.WriteLine(Object.ReferenceEquals(darren1, darren2)); //FalseConsole.WriteLine(darren1 == darren2); //TrueConsole.ReadKey();}}public class EquatableGuyWithOverload : EquatableGuy{public EquatableGuyWithOverload(string name, int age, int cash) : base(name, age, cash){}public static bool operator ==(EquatableGuyWithOverload left, EquatableGuyWithOverload right){if (Object.ReferenceEquals(left, null)) return false;else return left.Equals(right);}public static bool operator !=(EquatableGuyWithOverload left, EquatableGuyWithOverload right){return !(left == right);}public override bool Equals(object obj){return base.Equals(obj);}public override int GetHashCode(){return base.GetHashCode();}}

以上,子类EquatableGuyWithOverload重写了==操作符,由于其父类EquatableGuy已经重写了基类Object的Equals方法,所以在这里可以直接调用。
总结:通常情况下,使用==操作符比较值类型对象;使用实例方法Equals或静态方法Object.ReferenceEquals比较引用类型对象地址;如果想自定义比较逻辑,可以考虑实现IEquatable<>泛型接口,避免装箱、拆箱。
参考资料:
防止装箱落实到底,只做一半也是失败
Understanding C#: Equality, IEquatable, and Equals()
如何使用==操作符,Equals方法,ReferenceEquals方法,IEquatable接口比较2个对象的更多相关文章
- object.Equals与object.ReferenceEquals方法
object.Equals方法表达的是语义判等,不一定是引用判等. object.ReferenceEquals方法是肯定是引用判等. 怎么实现一个对象的值语义的 Equals方法?实验. MyCla ...
- java String的equals,intern方法(转载)
JAVA中的equals和==的区别 ==比较的是2个对象的地址,而equals比较的是2个对象的内容. 显然,当equals为true时,==不一定为true: 基础知识的重要性,希望引起大家的重视 ...
- Equals()和GetHashCode()方法深入了解
最近在看Jeffrey Richter的CLR Via C#,在看到GetHashCode()方法的时候,有一个地方不是特别明白,就是重写Equals()方法时为什么要把GetHashCode()方法 ...
- ( 转 ) 聊一聊C#的Equals()和GetHashCode()方法
聊一聊C#的Equals()和GetHashCode()方法 博客创建一年多,还是第一次写博文,有什么不对的地方还请多多指教. 关于这次写的内容可以说是老生长谈,百度一搜一大堆.大神可自行绕路. ...
- 聊一聊C#的Equals()和GetHashCode()方法
博客创建一年多,还是第一次写博文,有什么不对的地方还请多多指教. 关于这次写的内容可以说是老生长谈,百度一搜一大堆.大神可自行绕路. 最近在看Jeffrey Richter的CLR Via C#,在看 ...
- Java中的equals和hashCode方法
本文转载自:Java中的equals和hashCode方法详解 Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的,有时候我们需要实现特定需求,可能要 ...
- String equals()方法使用以及子串加密
String equals()方法的实现方法: 名称 说明 String.Equals (Object) 确定此 String 实例是否与指定的对象(也必须是 String)具有相同的值. Strin ...
- Java提高篇——equals()与hashCode()方法详解
java.lang.Object类中有两个非常重要的方法: 1 2 public boolean equals(Object obj) public int hashCode() Object类是类继 ...
- Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例(转)
Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例 原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...
随机推荐
- 在SQL中有时候我们需要查看现在正在SQL Server执行的命令
在SQL中有时候我们需要查看现在正在SQL Server执行的命令.在分析管理器或者Microsoft SQL Server Management Studio中,我们可以在"管理-SQL ...
- MySQL学习笔记:字符串前后补全0
遇到一个需求:不足6位的需要自动补全6位,使用函数LPAD()和RPAD()补全. LPAD(str, len, padstr) 用字符串padstr对str进行左边填充补全直至它的长度达到len个字 ...
- 转58同城 mysql规范
这里面都是一些很简单的规则,看似没有特别大的意义,但真实的不就是这么简单繁杂的工作吗? 军规适用场景:并发量大.数据量大的互联网业务 军规:介绍内容 解读:讲解原因,解读比军规更重要 一.基础规范 ( ...
- Redis(四)Redis高级
一Redis 数据备份与恢复 Redis SAVE 命令用于创建当前数据库的备份. 语法 redis Save 命令基本语法如下: redis 127.0.0.1:6379> SAVE 实例 r ...
- js 获取时间戳的方法
(new Date()).valueOf()1541569364658(new Date()).getTime()1541569372623Number(new Date())154156938662 ...
- 【POJ】1819.Disks
博客园的话插链接链接都是凉的= = 题解 我理解成能不能看到这个圆,除了最后几个圆特殊以外都是等价的,然而我凉了,因为我把圆当成线段来处理,但是,有可能一个圆完全被遮住了,还有一个缝隙,就WA了 计算 ...
- USACO 5.5 Twofive
TwofiveIOI 2001 In order to teach her young calvess the order of the letters in the alphabet, Bessie ...
- PCA(Principal Component Analysis)主成分分析
PCA的数学原理(非常值得阅读)!!!! PCA(Principal Component Analysis)是一种常用的数据分析方法.PCA通过线性变换将原始数据变换为一组各维度线性无关的表示,可 ...
- CentOS源码安装搭建LNMP全过程(包括nginx,mysql,php,svn)
服务器环境为:CentOS6.5 64位 目标:搭建LNMP(Linux + Nginx + MySQL + PHP +SVN),其中svn是用来代替ftp,方便开发中调试同步代码 相关目录:所有软件 ...
- 基于Apollo实现.NET Core微服务统一配置(测试环境-单机)
一.前言 注:此篇只是为测试环境下的快速入门.后续会给大家带来生产环境下得实战开发. 具体的大家可以去看官方推荐.非常的简单明了.以下介绍引用官方内容: Apollo(阿波罗)是携程框架部门研发的分布 ...