C#相等性 - 三个方法和一个接口

简介: C#相等性 - 三个方法和一个接口简介C#(.NET)的object类里面有三个关于判断相等性的方法:public virtual bool Equals(object obj)public static bool Equals(object objA, object objB)public static bool ReferenceEquals(object objA, object objB)还有一个接口:IEquatable也可以用来判断相等性。

C#相等性 - 三个方法和一个接口
简介
C#(.NET)的object类里面有三个关于判断相等性的方法:

public virtual bool Equals(object obj)
public static bool Equals(object objA, object objB)
public static bool ReferenceEquals(object objA, object objB)
还有一个接口:IEquatable也可以用来判断相等性。

virtual bool Equals()
比较自定义Class

比较这个Class的两个实例,它们的属性值是一样的:

输出结果:

之所以结果是False,是因为object.Equals()评估的是引用的相等性,除非进行了重写。

比较string
这是两个字符串,而且使用string.Copy()可以保证它们不指向同一个地址(如果不使用string.Copy(),而直接赋两个同样的值,那么可能会发生字符串驻留问题:https://www.cnblogs.com/artech/archive/2007/03/04/663728.aspx):

这时输出的结果是:

但是我们看一下string这个类,可以发现string有很多Equals()方法:

如果按照上面这么写的话,它并没有调用object.Equals()方法。所以我们改一下代码:

这时调用的是object.Equals()方法,它的输出依然是:

这是因为string类对object的Equals()方法进行了重写,重写后比较的是字符串的值。

除了string之外,delegates和Tuples也对object.Equals()方法进行了重写。不过对大部分的.NET类型来说,object.Equals()比较的是引用。

比较值类型
值类型是存放在Stack上面的,它们通常没有引用,除非你对它们进行装箱操作。

那么对值类型使用object.Equals()方法,应该没有什么意义。。。

有这么一个自定义的Struct:

然后进行两组比较:

输出结果是:

很显然,结果有点出乎我的意料,针对这个Struct类型,object.Equals()比较的是它们的值。

这是因为所有的struct都继承于System.ValueType,而System.ValueType继承于System.Object,System.ValueType它对object.Equals()方法进行了重写,重写的方法里会比较值类型里面所有的字段(Field),如果所有字段都相等,那么就返回true。

但是System.ValueType的重写是使用反射来找到所有的字段(Fields),所以性能比较差。

所以针对值类型最好的办法是自己重写一下Equals()方法。

总结
默认情况下,针对引用类型,object.Equals()比较的是引用;针对值类型,object.Equals()比较的是值。

但是所有的类型都可以重写object.Equals()方法,例如string。

静态的 Equals() 方法
比较null
使用object virtual的Equals()方法可以应付大部分情况,但是如果该引用是null,那么使用该方法就会报错了:

这时候我们就可以使用object类的静态Equals()方法:

(也可以不写object)

而结果当然是:

比较两个null

结果是:

在.NET/.NET Core 里面,null和null是相等的。

源码

静态Equals()方法的源码其实很简单,除了检查null之外,它会给出和virtual Equals()方法同样的结果。

如果你对virtual的Equals()方法进行了重写,而由于静态的Equals()方法就会调用重写的virtual Equals()方法,所以这两个方法要保持一贯性。

静态 Reference Equals() 方法
它和前两种方法有点像,但是也不尽相同。

虽然virtual和静态的Equals()方法通常会比较引用,但是virutal的方法可以被重写,从而比较的是值,例如string。所以使用ReferenceEquals()来比较两个变量是否指向同一个实例是更安全准确的。

看下面这两个比较:

第一个比较调用的是object的virtual Equals()方法,但是string对其进行了重写,比较的是值:

而第二个比较是object的静态的ReferenceEquals()方法,由于是静态的,所以没法重写:

而C#里的==是什么原理,以后再说。

IEquatable
System.Object的static bool Equals(object obj)这个方法,因为其参数是object类型,所以它可以对任何引用类型进行比较。但是如果想比较值类型的话,那么值类型就会被装箱,然后再进行比较。但是装箱的动作会有性能损耗,而之所以采用值类型的主要原因就是因为性能。所以这是一个问题。

再者,使用该方法来比较两个不相干的类型,比如Apple和Book这两个Class,比较的时候不会报错,但是这没有任何意义。这就是因为参数不是强类型,才会出现这些问题。

而IEquatable这个接口就可以解决这些问题。

它只定义了一个方法:bool Equals(T other)。

例子,三个int:

使用它的Equals()方法:

可以看到除了object.Equals(object obj)这个方法外,它还有一个Equals(int obj)这个方法,它的参数是强类型的,这是因为int实现了IEquatable接口。

而其源码大致如下:

所以平时比较int的时候使用==即可。

所有的原始类型都实现了IEquatable接口。int, byte...

而IEquatable对值类型非常有用。

但是对引用类型没有太大的用处,因为引用类型比较时不存在装箱问题,而且IEquatable在继承方面还是存在问题的,但是string还是实现了IEquatable接口,因为string是seal的,不存在继承。

需要注意的是如果实现了IEquatable,那么它的实现方法和重写的object.Equals()方法应该保持一致,做同样的事。
原文地址https://www.cnblogs.com/cgzl/p/10638553.html

相关文章
|
1月前
|
Java 调度 C#
C#学习系列相关之多线程(一)----常用多线程方法总结
C#学习系列相关之多线程(一)----常用多线程方法总结
|
1月前
|
C#
C#学习相关系列之数组---常用方法使用(二)
C#学习相关系列之数组---常用方法使用(二)
|
1月前
|
IDE C# 开发工具
C#系列之接口介绍
C#系列之接口介绍
|
1月前
|
存储 C# 数据库
C# 生成唯一ID,有哪些方法?
【2月更文挑战第12天】
157 0
|
3月前
|
达摩院 Linux API
阿里达摩院MindOpt求解器V1.1新增C#接口
阿里达摩院MindOpt求解器发布最新版本V1.1,增加了C#相关API和文档。优化求解器产品是求解优化问题的专业计算软件,可广泛各个行业。阿里达摩院从2019年投入自研MindOpt优化求解器,截止目前经历27个版本的迭代,取得了多项国内和国际第一的成绩。就在上个月,2023年12月,在工信部产业发展促进中心等单位主办的首届能源电子产业创新大赛上,MindOpt获得电力用国产求解器第一名。本文将为C#开发者讲述如何下载安装MindOpt和C#案例源代码。
139 3
阿里达摩院MindOpt求解器V1.1新增C#接口
|
3月前
|
编译器 C# 开发者
C# 11.0中的新特性:覆盖默认接口方法
C# 11.0进一步增强了接口的灵活性,引入了覆盖默认接口方法的能力。这一新特性允许类在实现接口时,不仅可以提供接口中未实现的方法的具体实现,还可以覆盖接口中定义的默认方法实现。本文将详细介绍C# 11.0中接口默认方法覆盖的工作原理、使用场景及其对现有代码的影响,帮助开发者更好地理解和应用这一新功能。
|
3月前
|
安全 C# 开发者
C#中的默认接口方法:接口演化的新篇章
【1月更文挑战第11天】本文探讨了C# 8.0中引入的默认接口方法,这一特性允许在接口中定义具有默认实现的方法。文章介绍了默认接口方法的语法、使用场景,以及它们如何影响接口的设计和实现,同时讨论了默认接口方法带来的好处和潜在的陷阱。
|
1月前
|
C#
C#学习相关系列之数据类型类----嵌套类和嵌套方法(三)
C#学习相关系列之数据类型类----嵌套类和嵌套方法(三)
|
1月前
|
开发框架 .NET C#
C#学习相关系列之Linq常用方法---排序(一)
C#学习相关系列之Linq常用方法---排序(一)
|
1月前
|
开发框架 .NET 编译器
C#学习相关系列之匿名方法和Lambda表达式
C#学习相关系列之匿名方法和Lambda表达式