当前位置:
首页 > temp > 简明python教程 >
-
自定义值类型一定不要忘了重写Equals,否则性能和空间双双堪忧
一:背景
1. 讲故事
曾今在项目中发现有同事自定义结构体的时候,居然没有重写Equals方法,比如下面这段代码:
static void Main(string[] args)
{
var list = Enumerable.Range(0, 1000).Select(m => new Point(m, m)).ToList();
var item = list.FirstOrDefault(m => m.Equals(new Point(int.MaxValue, int.MaxValue)));
Console.ReadLine();
}
public struct Point
{
public int x;
public int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
这代码貌似也没啥什么问题,好像大家平时也是这么写,没关系,有没有问题,跑一下再用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
翻出来看看便知:
public abstract class ValueType
{
public override bool Equals(object obj)
{
if (CanCompareBits(this)) {return FastEqualsCheck(this, obj);}
FieldInfo[] fields = runtimeType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
for (int i = 0; i < fields.Length; i++)
{
object obj2 = ((RtFieldInfo)fields[i]).UnsafeGetValue(this);
object obj3 = ((RtFieldInfo)fields[i]).UnsafeGetValue(obj);
...
}
return true;
}
}
从上面代码中可以看出有如下三点信息:
<1> 通用的 equals
方法接收object类型,参数装箱一次。
<2> CanCompareBits,FastEqualsCheck
都是采用object类型,this
也需要装箱一次。
<3> 有两种比较方式,要么采用 FastEqualsCheck
比较,要么采用反射
比较,我去.... 反射就玩大了。
综合来看确实没毛病, equals
会把比较的两个对象都进行装箱。
2. 改进方案
问题找到了,解决起来就简单了,不走这个通用的 equals 不就行啦,我自定义一个equals方法,然后跑一下代码。
public bool Equals(Point other)
{
return this.x == other.x && this.y == other.y;
}
可以看到走了我的自定义的Equals,
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
SQL Server -- 解决存储过程传入参数作为s
关于JS定时器的整理
JS中使用Promise.all控制所有的异步请求都完
js中字符串的方法
import-local执行流程与node模块路径解析流程
检测数据类型的四种方法
js中数组的方法,32种方法
前端操作方法
数据类型
window.localStorage.setItem 和 localStorage.setIte
如何完美解决前端数字计算精度丢失与数