【读书笔记】《Unity3D高级编程: 主程手记》01:C#技术总结
本文是《Unity3D高级编程: 主程手记》的读书笔记(后面统一简称主程手记),主程手记是一本很全面的Unity进阶的书记,值得所有Unity开发都买一本读一读。博主这里将一些自己经常用得上的内容重新做了遍归纳总结,做成读书笔记也方便自己查阅。
1. List的要点
原书将List的源码逐个功能进行了分析,这里就不重复引用了,直接附上源码。List的要点如果用一句话总结那就是:List是由数组实现的,而不链表。也是因为底层逻辑是数组的原因,List的效率并不高。
Add、Insert: 这两个方法的本质还是数组拷贝,List的扩容是最需要注意的地方,假如创建一个空的List,默认容量为0,在Add或者Insert元素时,会先检查容量是否足够,不足则进行2的指数级别扩容,比如2、4、8、16、32…依次扩容。所以在创建List的时候分配一个预计的默认容量能减少GC的压力,也避免浪费内存。
Remove: 同上,本质还是数组拷贝。
Contains: 使用了线性查找的方式比较元素,效率为O(n)。
Find: 同上。
Enumerator: 原书表示foreach会增加Enumerator实例,造成GC压力,在 .NET4.0以后修复了此问题。
Sort: 使用的是快速排序,效率为O(nLgn)。
2. Dictionary的要点
原书讲Dictionary解析的很好,这里博主自己做一些总结。Dictionary是Key和Value一一映射的字典型数据结构,对于Key,DIctionary做的是加入容器时对Key做Hash运算。在源码中,哈希冲突才拉链法贯穿了整个底层架构,在使用Dictionary时要尽量避免Key的Hash冲突。
- Add、Insert: Add和Insert的扩容方式为初始化时为0,自动扩容按照3、7、17、37的方式扩容,所以和List相同,初始化的时候给定一个大致的数量更有效率。
- Remove: Remove并不会删减内存。
- ContainsKey: 本质是用Key值得到哈希地址开始查询,用数值的方式做Key更有效率。
3. 装箱、拆箱
C#中有引用类型(Reference Type)和值类型(Value Type)
值类型:常见的有
int
、float
、bool
、char
、struct
、enum
等)。值类型数据在栈(stack)上分配内存空间。当一个值类型变量在赋值另一个变量时,时会进行数据的复制,两个变量都会有独立的内存空间和数据副本,修改一个不会影响另一个。引用类型:常见的引用类型包括(
class
、interface
、array
、delegate
)等,引用类型在堆(heap)上分配内存。当一个引用类型的变量赋值给另一个变量时,将复制内存地址的引用而不是值,所以两个变量指向同一个内存地址,修改一个变量会影响所有引用该数据的变量。
装箱就是把值类型实列转换为引用类型,拆箱正好相反。下面用代码分别展示一下装箱和拆箱。
1 | int a = 10; |
1 | int a = 10; |
在Unity中一般只有当接口需要更加通用的时候才会需要装箱,比如一个接口参数为object,这样就支持传入任意类型。但是装箱、拆箱会消耗CPU产生内存碎片,原书中提到三种方法可以优化,分别是:
- Struct 通过重载函数来避免拆箱、装箱。
- 通过泛型来避免拆箱、装箱。
- 通过继承统一的接口提前拆箱、装箱,避免多次重复拆箱、装箱。
5. 浮点数
浮点数也是一个老生常谈的问题了,浮点数用起来比较矛盾,可以说是既精确又不精确,一方面能保证一定的精度,另一方面呢又不能拿去比较(有些新手用==比较,这不就寄了嘛),每次计算结果又不同。通过学习主程手记,总结了浮点数的特征,同时学习到几种浮点数的替代用法,很有价值。
浮点数的特征: float为4字节(32位),范围为-3.4×10³⁸ 到 +3.4×10³⁸ 。double为8字节(64位),范围为-1.7×10³⁰⁸ 到 +1.7×10³⁰⁸。浮点数有三个重要特征:数值比较不相等;数值计算不确定;不同设备计算结果不同。
几种浮点数的优化方法:
- 方法一:用int或long替代浮点数,比如将9.9乘以1000变成99000,这样就能保证3位的精度了,实际使用要注意int和long的精度上限问题。
- 方法二:用定点数保持一致性并缩小精度问题,所谓的定点数,就是将浮点数的整数位和小数位拆开操作,可以尝试在unity中自己封装。
- 方法三:用字符串代替浮点数,CPU和内存性能消耗都大,需要高精度的时候可以考虑。