当前位置: 移动技术网 > IT编程>开发语言>c# > c# 自定义值类型一定不要忘了重写Equals,否则性能和空间双双堪忧

c# 自定义值类型一定不要忘了重写Equals,否则性能和空间双双堪忧

2020年11月13日  | 移动技术网IT编程  | 我要评论
一:背景1. 讲故事曾今在项目中发现有同事自定义结构体的时候,居然没有重写equals方法,比如下面这段代码:这代码貌似也没啥什么问题,好像大家平时也是这么写,没关系,有没有问题,跑一下再用windb

一:背景

1. 讲故事

曾今在项目中发现有同事自定义结构体的时候,居然没有重写equals方法,比如下面这段代码:

这代码貌似也没啥什么问题,好像大家平时也是这么写,没关系,有没有问题,跑一下再用windbg看一下。

0:000> !dumpheap -stat
statistics:
mt count totalsize class name
00007ff8826fba20 10 16592 consoleapp6.point[]
00007ff8e0055e70 6 35448 system.object[]
00007ff8826f5b50 2000 48000 consoleapp6.point

0:000> !dumpheap -mt 00007ff8826f5b50
address mt size
0000020d00006fe0 00007ff8826f5b50 24

0:000> !do 0000020d00006fe0
name: consoleapp6.point
fields:
mt field offset type vt attr value name
00007ff8e00585a0 4000001 8 system.int32 1 instance 0 x
00007ff8e00585a0 4000002 c system.int32 1 instance 0 y

从上面的输出不知道你看出问题了没有? 托管堆上居然有2000个point,而且还可以用 !do 打出来,说明这些都是引用类型。。。这些引用类型哪里来的? 看代码应该是 equals 比较时产生的,一次比较就有2个point被装箱放到托管堆上,这下惨了,,,而且大家应该知道引用对象本身还有(8+8) byte 自带开销,这在时间和空间上都是巨大的浪费呀。。。

二: 探究默认的equals实现

1. 寻找valuetype的equals实现

为什么会这样呢? 我们知道equals是继承自valuetype的,所以把 valuetype 翻出来看看便知:

从上面代码中可以看出有如下三点信息:

<1> 通用的 equals 方法接收object类型,参数装箱一次。

<2> cancomparebits,fastequalscheck 都是采用object类型,this也需要装箱一次。

<3> 有两种比较方式,要么采用 fastequalscheck 比较,要么采用反射比较,我去.... 反射就玩大了。

综合来看确实没毛病, equals 会把比较的两个对象都进行装箱。

2. 改进方案

问题找到了,解决起来就简单了,不走这个通用的 equals 不就行啦,我自定义一个equals方法,然后跑一下代码。

可以看到走了我的自定义的equals,🐮👃。 貌似问题就这样简单粗暴的解决了,真开心,打脸时刻开始。。。

三:真的解决问题了吗?

1. 遇到问题

很多时候我们会定义各种泛型类,在泛型操作中通常会涉及到t之间的 equals, 比如下面我设计的一段代码,为了方便,我把point的默认equals也重写一下。

从输出结果看,还是走了通用的equals方法,这就尴尬了,为什么会这样呢?

2. 从fcl的值类型实现上寻找问题

有时候苦思冥想找不出问题,突然灵光一现,fcl中不也有一些自定义值类型吗? 比如 int,long,decimal,何不看它们是怎么实现的,寻找寻找灵感, 对吧。。。说干就干,把 int32 源码翻出来。

我去,还是int🐮👃,貌似我的point就比int少了接口实现,问题应该就出在这里,而且最后一个泛型接口iequatable<int>特别显眼,看下定义:

这个泛型接口也仅仅只有一个equals方法,不过灵感告诉我,貌似。。。也许。。。应该。。。就是这个泛型的equals是用来解决泛型情况下的equals比较。

3. 补上 iequatable 接口

有了这个思路,我也跟fcl学,让point实现 iequatable<t>接口,然后在tproxy<t>代理类中约束下必须实现iequatable<t>,修改代码如下:

然后将程序跑起来,如下图:

🐮👃,虽然是成功了,但有一个地方让我不是很舒服,就是上面的第二行代码,在 tproxy<t> 处约束了t,因为我翻看list的实现也没做这样的泛型约束呀,可能有点强迫症吧,贴一下代码给大家看看。

然后我继续模仿list,把 tproxy<t> 上的t约束去掉,结果就出问题了,又回到了 通用equals

4. 从list的contains源码中寻找答案

好奇心再次驱使我寻找list中是如何做到的,为了能看到list中原生方法,修改代码如下,从contains方法入手。

我也是太好奇了,翻看下 contains 的源码,简化后实现如下。

原来list是在进行 equals比较之前,自己构建了一个泛型比较器equalitycomparer<t>,🐮👃,然后继续追一下代码。

因为这里的runtimetype实现了iequatable<t>接口,所以代码返回了一个泛型比较器:genericequalitycomparer<t>,然后我们继续查看这个泛型比较器是咋样的。

从图中可以看到最终还是对t进行了iequatable<t>约束,不过这里给提取出来了,还是挺厉害的,然后我也学的模仿一下:

可以看到也走了我的自定义实现,两种方式大家都可以用哈😁😁😁。

最后要注意一点的是,当你重写了equals之后,编译器会告知你最好也把 gethashcode重写一下,只是建议,如果看不惯这个提示,尽可能自定义gethashcode方法让hashcode分布的均匀一点。

四:总结

一定要实现自定义值类型的 equals方法,人家的 equals方法是用来兜底的,一次比较两次装箱,对你的程序可是双杀哦😁😁😁。

以上就是c# 自定义值类型一定不要忘了重写equals,否则性能和空间双双堪忧的详细内容,更多关于c# 自定义值类型的资料请关注移动技术网其它相关文章!

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

  • 如何使用C# 捕获进程输出

    intro很多时候我们可能会需要执行一段命令获取一个输出,遇到的比较典型的就是之前我们需要用 ffmpeg 实现视频的编码压缩水印等一系列操作,当时使用的是 f... [阅读全文]
  • 全面分析c# LINQ

    大家好,这是 [c#.net 拾遗补漏] 系列的第 08 篇文章,今天讲 c# 强大的 linq 查询。linq 是我最喜欢的 c# 语言特性之一。linq 是... [阅读全文]
  • C# DataTable常见用法汇总

    c# datatable 的常见用法:(1)新建数据表。(2)向表添加列。(3)设置表特定行与列的数据值。(4)将某行数据加入到表。(5)合并表。(6)复制表。... [阅读全文]
  • C# 如何解析获取Url参数值

    今天遇到一个需求,需要处理通过接口传过来的一个参数,参数内容为一个拼接好的url地址,且该地址还会携带了一些额外的参数,包括但不限于数字,字符串,json串。样... [阅读全文]
  • C# 实现dataGridView选中一行右键出现菜单的示例代码

    在窗体中添加datagridview控件和contextmenustrip1控件,修改datagridview属性,将contextmenustrip控件绑定d... [阅读全文]
  • WPF实现手风琴式轮播图切换效果

    WPF实现手风琴式轮播图切换效果

    本文实例为大家分享了wpf实现轮播图切换效果的具体代码,供大家参考,具体内容如下实现效果如下:步骤:1、自定义控件myimagecontrol实现图片的裁切和动... [阅读全文]
  • WPF实现3D翻牌式倒计时特效

    WPF实现3D翻牌式倒计时特效

    本文实例为大家分享了wpf实现3d翻牌式倒计时的具体代码,供大家参考,具体内容如下实现效果如下:思路:使用自定义控件,设置一个背板 mycardcontrolb... [阅读全文]
  • WPF实现平面三角形3D运动效果

    WPF实现平面三角形3D运动效果

    本文实例为大家分享了wpf实现平面三角形3d运动效果的具体代码,供大家参考,具体内容如下实现效果如下:思路:封装三角形三个顶点和路径的三角形类,图形渲染时同步更... [阅读全文]
  • WPF实现3D粒子波浪效果

    WPF实现3D粒子波浪效果

    本文实例为大家分享了wpf实现3d粒子波浪效果的具体代码,供大家参考,具体内容如下实现效果如下:步骤:1、3d粒子类particle.cs2、粒子系统parti... [阅读全文]
  • 谈谈c#中的索引器

    概念索引器(indexer) 允许类中的对象可以像数组那样方便、直观的被引用。当为类定义一个索引器时,该类的行为就会像一个 虚拟数组(virtual array... [阅读全文]
验证码:
移动技术网