欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C# - 为引用类型重定义相等性

程序员文章站 2022-07-07 08:19:04
通常情况下引用类型的相等性是不应该被重定义/重写的。 例如两个引用类型的变量 x 和 y,如果这样写:if(x == y) {...},那么大家都明白,这个比较的是引用的相等性。 但是有少数情况下,也可以为引用类型重写相等性。 例如这个类: 这个类里面只有两个string类型的属性和字段,那么对它的 ......

通常情况下引用类型的相等性是不应该被重定义/重写的。

例如两个引用类型的变量 x 和 y,如果这样写:if(x == y) {...},那么大家都明白,这个比较的是引用的相等性。

但是有少数情况下,也可以为引用类型重写相等性。

例如这个类:

C# - 为引用类型重定义相等性

这个类里面只有两个string类型的属性和字段,那么对它的相等性来说,更合理的是去比较值,而不是引用。

 

还有一种情况,就是表示数学的引用类型。

例如有一个类表示矩阵 matrix,那么这样写 if(matrix1 == matrix2) {...} 更适合表示它们两个的值相等。

 

上述的这两个例子其实也不是十分的必要。所以想为引用类型重写相等性的时候还是应该先想好,重写后是否能够更加的直观,使理解便得更简单了。

实际上如果想比较两个应用类型里面的值是否相等,你不必非得去重写那些相等性的方法,你可以通过实现iequalitycomparer<t>接口来写一个单独的相等性比较器。但是这样的话不能使用==操作符,需要这样写:if(eqcomparer.equals(x, y)) {...}

 

为引用类型重写相等性

一个类:

C# - 为引用类型重定义相等性

首先重写object.equals()方法:

C# - 为引用类型重定义相等性

这个逻辑比较简单,就是判断null,引用和类型,然后再判断各个属性(字段)的值是否相等。

 

然后还需要重写object.gethashcode()方法:

C# - 为引用类型重定义相等性

这个采用了resharper生成的方法,以前说过,就不再介绍了。

 

最佳实践还要求重写c#的==操作符:

C# - 为引用类型重定义相等性

当然配套的!=也必须重写。

 

在之前重写值类型相等性的文章里,我还为值类型实现了iequatable<t>接口,而对于引用类型来说,就没有必要去实现该接口了,可以把相等性判断逻辑放在object.equals()方法里。

 

派生类

这是上面citizen类的一个子类:

C# - 为引用类型重定义相等性

 

下面我重写object.equals() 方法:

C# - 为引用类型重定义相等性

大部分逻辑都在base.equals()方法里了,首先如果父类的equals()方法返回false,那么下面也就不用做啥了。但是如果父类equals()认为这两个实例是相等的,这就意味着父类里所有的相等性检查都通过了,然后我们仍然需要检查派生类里面的独有字段(属性),而这个例子里只有一个字段(属性)。

然后别忘了实现gethashcode()方法:

C# - 为引用类型重定义相等性

(resharper生成的代码)

这个方法里使用了父类的gethashcode()方法,把它按位异或idcard的gethashcode()的结果。

 

然后实现==和!=操作符:

C# - 为引用类型重定义相等性

好,现在我们来测试一下:

C# - 为引用类型重定义相等性

其结果如下:

C# - 为引用类型重定义相等性

这个结果还都是对值进行比较的,符合预期。

 

然后你可能以为这样实现没有问题了。。。。

陷阱 

现在我在citizen这个父类里修改一下==的实现,我想让它更有效率:

C# - 为引用类型重定义相等性

然后我再执行和上面同样的测试代码,其结果输入是:

 C# - 为引用类型重定义相等性