【Unity主程手记(摘录)】第一章(四) - 委托和事件、拆箱和装箱

第一章(四) - 委托和事件、拆箱和装箱

提示:个人学习总结,如有错误,敬请指正。


文章目录

  • 第一章(四) - 委托和事件、拆箱和装箱
  • 一、委托(delegate)
  • 二、event
  • 三、Action和Func
  • 四、装箱和拆箱
  • 五、装箱、拆箱对执行效率有哪些影响,如何优化。
  • 六、装箱、拆箱的实现细节(待更新)
  • 附录


一、委托(delegate)

delegate的实例,从功能上来讲,类似于C++的函数指针。
可以把委托理解为一个函数容器,由于委托可以被直接赋值,所以会有函数丢失的风险,


二、event

event 很简单,它在委托delegate上,又做了一次封装,这次封装的意义是,限制用户直接操作delegate委托实例中变量的权限。
其不在可以进行 =操作,但仍然保留+= 和 -=;


三、Action和Func

  • Action:C#封装好的委托写法,也会有函数丢失的风险。
  • Func: 带返回值

四、装箱和拆箱

  • 装箱:值类型实例转换为引用类型实例
  • 拆箱:值类型实例转换为引用类型实例

值类型是在声明时就初始化了,因为它一旦声明就有了自己的空间因此它不可能为null,也不能为null。

引用类型在分配内存后,它其实只是一个空壳子,可以认为是指针,初始化后不指向任何空间,因此默认为null。

	栈是本着先进后出的数据结构(LIFO)原则的存储机制,
	它是一段连续的内存,所以对栈数据的定位比较快速,
	而堆则是随机分配的空间, 处理的数据比较多, 无论如何, 至少要两次定位。
	堆内存的创建和删除节点的时间复杂度是O(logn)。栈创建和删除的时间复杂度则是O(1),栈速度更快。

	那么既然栈速度这么快,全部用栈不就好了。
	这又涉及到生命周期问题,由于栈中的生命周期是必须确定的,销毁时必须按次序销毁,
	从最后分配的块部分开始销毁,创建后什么时候销毁必须是一个定量,所以在分配和销毁时不灵活,
	基本都用于函数调用和递归调用中,这些生命周期比较确定的地方。
	相反堆内存可以存放生命周期不确定的内存块,满足当需要删除时再删除的需求,
	所以堆内存相对于全局类型的内存块更适合,分配和销毁更灵活。

值类型的数据存储在stack上,直接持有。

引用类型的真实数据存储在heap,持有该地址的指针。

装箱的内部操作:

装箱:
根据相应的值类型在堆中分配一个值类型内存块,再将数据拷贝给它。按三步进行。

  1. 第一步:在堆内存中新分配一个内存块(大小为值类型实例大小加上一个方法表指针和一个SyncBlockIndex)。
  2. 第二步:将值类型的实例字段拷贝到新分配的内存块中。
  3. 第三步:返回内存堆中新分配对象的地址。这个地址就是一个指向对象的引用了。

拆箱:
则更为简单点,先检查对象实例,确保它是给定值类型的一个装箱值,再将该值从实例复制到值类型变量的内存块中。


五、装箱、拆箱对执行效率有哪些影响,如何优化。

影响:由于装箱、拆箱时生成的是全新的对象,不断得分配和销毁内存会不但大量消耗CPU,也同时增加了内存碎片,降低了性能。

避免方法:

  • Struct 通过重载函数来避免拆箱、装箱。
  • 使用泛型
  • 通过统一实现的接口提前拆装箱

六、装箱、拆箱的实现细节(待更新)


附录

主程手记

你可能感兴趣的:(#,unity,高级编程,主程手记,unity,c#)