C# 对象相等性判断和同一性判断

时间:2022-04-24
本文章向大家介绍C# 对象相等性判断和同一性判断,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在日常开发中经常需要编写代码比较不同的对象。例如,有时需要将对象都放到一个集合中,并编写代码对集合中的对象进行排序、搜索或者比较。

System.Object类有两个Equals方法,如下:

1、实例Equals方法(可重写),代码如下:

public virtual bool equals(object obj) => RuntimeHelpers.Equals(this, obj)

再看看RuntimeHelpers.Equlas里面调的是什么方法,代码如下:

[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical]
public static extern bool Equals(object o1, object o2);

ok,这里的extern关键字告诉你,接下来的不用你考虑了!

2、静态方法Equals方法,代码如下:

public static bool Equals(object objA,object objB)=>
((objA==objB) || (((objA!=null) && (objB!=null)) &&  objA.Equals(objB)))

继续深入解析代码,发现objA.Equals调用了上面的实例Equals方法.其实就是在实例Equals方法的基础上做了非空判断.然后方法做了静态化.

到这里源码解析完毕,由于到extern这一步解析不下去了(博主实力有限),如有知道的请告知!万分感谢!

由于类型能够重写Equals方法,所以Equals方法的逻辑远比想象的要复杂.下面来举几个例子:

1、由于类型能够重写Equals方法,所以不能使用它来测试同一性,为了解决这个问题,Object类型提供了ReferenceEquals方法来比较两个对象的同一性,ReferenceEquals代码如下:

public static bool ReferenceEquals(object objA,object objB)=>(objA==objB)

注:判断两个对象的"同一性"不应该使用C#的==操作符(除非将两个操作符进行装箱转换为Object),因为某个操作数可能重载了==操作符

2、System.ValueType(所有值类型的基类)就重写了Object的Equals方法,并对两个对象进行了正确的值相等检查而不是同一性检查.代码如下:

public bool Equals(uint obj)=>(this == obj);

==操作符进行的值检查.

ValueType.Equals内部会进行一下操作:

1、如果obj实参为null,就返回false;

2、如果this和obj引用的是不同的对象,返回false;

3、针对类型定义的每个实例字段,都将this对象中的值与obj对象中的值进行比较(通过调用对象的Equals方法)。任何字段不相等,就返回false.

4、返回true,ValueType的Equals方法不掉用Object的Equals方法.

上述3步骤,是通过反射实现,由于CLR的反射机制效率不高,所以在定义自己的值类型的时候,应重写Equals方法来提供自己的实现,从而提高自己类型进行值类型比较时的性能.注:自己的实现不用调用base.Equals().

当我们定义自己的类型时,重写的Equals方法要符合下面几个特性:

1、Equals必须自反 x.Equals(x)肯定返回true.

2、Equlas必须对称 x.Equals(y)和y.Equals(x)必须返回相同的值

3、Equals必须可传递 x.Equals(y)返回true,y.Equals(z)返回true则x.Equals(z)也必须返回true.

4、Equals必须一致,比较的两个值不变,Equals返回值(true或false)也不能变

如果实现的Equals方法不符合上述特性,应用程序就会行为失常.

重写Equals方法必须做以下几件事

1、让类型实现System.IEquatable<T>接口的Equals方法

这个泛型接口允许定义类型安全的Equals方法,通常实现的Equals方法应获取一个Object参数,以便在内部调用类型安全的Equals方法.

2、重载==和!=操作符方法

通常应实现这些操作符方法,在内部调用类型安全的Equals方法.