C#中几种比较是否相等的方法(Equals,ReferenceEquals,==)

嘎子 C# 2016-12-04 1,117 次浏览 没有评论

这个文章是之前遇到的一个问题,自己就记录在案了,现在分享出来给大家,当然这里面的内容在很多地方也能找到,并非常所有的东西都是原创,但我会尽量的把问题说的非常明白。

我这个人就这样,不喜欢看过程,只喜欢结果,先把结果说出来:

ReferenceEquals是这里面最简单的一个了,是静态方法,不能重写,比较引用同一个对象。最常用的是比较一个对象是否为null。具体细节下面会细说。

Equals是一个实例方法,所以Equals的行为与每一个类相关,在.Net的自带类中,类比较引用,值比较值。而且可以重载,支持更多的比较方式。

==运算符和Equals一样,可以重写,可以自己定义。在没有重写的==的前提下,==直接调用Equals方法。

最后需要的是,如果重载了Equals,则最好是重载GetHashCode,必须重载==运算符。

先说一个大家最容易出错的地方,那就是判断一个对象是否为null,如果这个对象为obj,我们常用如下代码:

但这种比较方法如果在没有重载的情况下会出现异常,因为!=和==一样,调用了Equals方法,但是如果obj为null的话,他是没有Equals方法的,所以异常。有两种方法可以解决这个问题:

第一种方法大家可能比较明白,对比引用,可以返回正确的结果。但对于第二种大家可能比较疑惑,我也觉得这是一个非常让人不爽的地方,Equals是一个双面间谍,他既是一个实例方法又是一个静态方法,所以,我个人不推荐使用第二种方法,容易造成疑惑,而且如果对实例方法Equals进行重载可能还会影响静态方法的Equals。其他的我也按步就班的给大家介绍一下:

ReferenceEquals用于比较引用类型的引用是是否指向同一个对象。它只能比较引用类型。当把值类型传给它的时候永远都会返回false,因为值类型作为参数的时候首先会装箱,经过装箱的值类型哪怕是指相等,但是也是两个不同的对象,所以变量是指向不同的对象,所以永远返回false。

int x = 10;
int y = 10;
bool b1 = object.ReferenceEquals(x,y);
这里结果肯定是返回false,但是如果是比较引用类型,如果是两个引用指向同一个对象,则为true。

我们还是先定义实体类

调用代码

我们可以发现第一个返回false,第二个返回true。那么如果其中有一个对象为null,或者两个对象都为null呢?结果会为false,如果两个都为null呢?结果为true。他们不会引发异常。

实例Equals

实例Equals算是比较复杂的一个比较方法。实例Equals可以比较引用是否指向同一个对象,同时可以按值来比较对象。如果要按值比较对象,我们就需要重载Equals对象来实现我们的比较逻辑。同时Equals默认也支持比较值类型的相等。那么我们该怎么重载Equals来让对象具有值相等性的比较呢?

MSDN给我们列出了一些准则

除涉及浮点型的情况外,x.Equals(x) 都返回 true。

x.Equals(y) 返回与 y.Equals(x) 相同的值。

如果 x 和 y 都为 NaN,则 x.Equals(y) 返回 true。

当且仅当 x.Equals(z) 返回 true 时,(x.Equals(y) && y.Equals(z)) 才返回 true。

只要不修改 x 和 y 引用的对象,对 x.Equals(y) 的相继调用将返回相同的值。

x.Equals(nullNothingnullptrnull 引用(在 Visual Basic 中为 Nothing)) 返回 false。

我们来看看重写代码

调用代码

我们可以看到结果,第一个为true,第二个为false。重载的时候不能出现异常。那么如果有一个类继承Person呢,我们又改如何比较派生类。

public class Student:Person
{
private int _studentNumber;

public int StudentNumber
{
get { return _studentNumber; }
set { _studentNumber = value; }
}

public Student() { }

public Student(int personId, string firstName, string lastName, int studentNumber)
{
this.PersonId = personId;
this.FirstName = firstName;
this.LastName = lastName;
this._studentNumber = studentNumber;
}

public override bool Equals(object obj)
{
if (obj != null && obj is Person)
{
Student s = obj as Student;
return base.Equals(obj)&&StudentNumber==s.StudentNumber;
}
else
{
return false;
}
}

public override int GetHashCode()
{
return base.GetHashCode()^StudentNumber;
}
}
调用代码

Student s1 = new Student(1, "Edrick", "Liu", 1);
Student s2 = new Student(2, "Meci", "Luo", 2);
Student s3 = new Student(1, "Edrick", "Liu", 1);

Console.WriteLine(s1.Equals(s2));
Console.WriteLine(s1.Equals(s3));
我们只需要调用父类的Equals方法和比较派生类中的新值。

静态Equals

这个方法算是比较有趣的一个方法了。这个方法也是静态的,它能比较引用,能比较值类型。如果比较的类型重载了实例的Equals,那么它也能也比较对象的值。所以它返回true有三种情况。

1,引用指向同一个对象

2,比较两个null

3,重载了Equals的实例方法返回true

Student s1 = new Student(1, "Edrick", "Liu", 1);
Student s2 = new Student(2, "Meci", "Luo", 2);
Student s3 = new Student(1, "Edrick", "Liu", 1);
Student s4 = s3;

Console.WriteLine(object.Equals(s1,s3));
Console.WriteLine(object.Equals(s4, s3));
这两个都为true,这里静态的Equals跟静态的EqualsReference有一个区别,静态的Equals如果有一个参数为null会抛出异常。

下面讨论一个有趣的现象,如果重载了Equals但是没有重载==运算符,会发生什么

Student s1 = new Student(1, "Edrick", "Liu", 1);
Student s2 = new Student(2, "Meci", "Luo", 2);
Student s3 = new Student(1, "Edrick", "Liu", 1);
Student s4 = s3;

Console.WriteLine(s1==s3);
Console.WriteLine(s3==s4);
第一个为false,第二个为true。这显然不符合我们意图,所以重载了Equals必须重载==,同样重载了==也必须重载Equals。这样符合我们的意图,也能确保在使用集合的时候,代码能按照我们的意图工作。因为集合coll[0]==co[0]其实比较的是引用,但是如果我们的Equals比较的是对象的值那么最后代码还是不能按照我的期望的运行。

==运算符

==号运算符其实跟实例的Equals没有多大的区别,==是运算符,而Equals是方法。他们都可以重写。默认都能比较引用和比较值。关于==的重载可以参考运算符一文中的运算符重载。

原创文章,文章首发于:Riley Ge (@rileyge) — Steemit

原创文章,转载请注明: 转载自TsonTec:测量解决方案提供者

本文链接地址: C#中几种比较是否相等的方法(Equals,ReferenceEquals,==)

相关主题

  • Kotlin中的data类–为数据而生,为数据而美2017-05-27 Kotlin中的data类–为数据而生,为数据而美 (0)
    我们在写程序时,不可避免的会进行数据的操纵。在进行数据操纵时,我们会专门的编写一些类。如果这些类写的多了,你会发现这些类都有一些比较类似的特点,因为这些特点的 […]
  • 也赶个时髦,说说Kotlin那些事2017-05-25 也赶个时髦,说说Kotlin那些事 (1)
    自己之前很长时间都在用C#来进行桌面的编程,所以非常喜欢C#那种简洁、简单的语言风格。虽然有很多人在说C#的效率有问题(当然,Microsoft也一直在说C# […]
  • DB4O在进行更新时只能在同一个session中的问题及改进方法2016-07-07 DB4O在进行更新时只能在同一个session中的问题及改进方法 (2)
    之前总是发现DB4O的好,但今天在使用DB4O的时候时候发现了一个非常大的不完善的地方,那就是如果要更新数据库,那么就必须在同一个session。在数据库中s […]
  • 用C#获取NIST时间2016-02-05 用C#获取NIST时间 (0)
    NIST为National Institute of Standards and […]
  • 返回null还是抛出Exception2016-03-22 返回null还是抛出Exception (2)
    我自己在写代码的时候也经常会写return null,但仔细想想这真的好吗? public User GetUser(string […]
  • C#重载操作符==和!=时注意问题2016-03-12 C#重载操作符==和!=时注意问题 (0)
    1、大家一定要明确一点就是如果两个类在没有重载==和!=时用这两个运算符进行比较,那么只有引用相同的地方的时候才返回true,否则(不管里面的内容是否相等)返 […]

说点什么

您将是第一位评论人!

提醒
avatar
回顶部