视频:装箱和拆箱

ArrayList 集合在存数据的时候,会把一切对象都隐式转换成 object 类型,这对于引用类型来说没什么,但是对于值类型来说,需要做额外的操作。
我们知道,作为局部变量的值类型会存放在内存栈上,而引用类型存放在内存堆中,当我们要把值类型封装到 object 时,我们需要把值从内存栈转移到内存堆上,这个过程就是装箱,而这个过程的逆过程,我们称之为拆箱。
官方解释:
装箱是将值类型转换为 object 类型或由此值类型实现的任何接口类型的过程,(公共语言运行库 (CLR) 对值类型进行装箱时)会将值包装在 System.Object 实例中并将其存储在托管堆中。拆箱将从对象中提取值类型。
装箱是隐式的;取消装箱是显式的。
装箱和取消装箱的概念是类型系统 C# 统一视图的基础,其中任一类型的值都被视为一个对象。
简单理解:
装箱是把值类型转换成引用类型——值类型 --> 引用类型
拆箱是把引用类型转换成值类型——引用类型 --> 值类型
装箱和拆箱的过程:
装箱过程:首先需要在堆内存上分配内存空间,然后构造一个新的 Object 对象,再把栈中的值类型数据封装到引用类型中。

拆箱过程:需要先在栈中分配一个相同值类型的空间,然后把堆内存中的引用类型强制转换成对应的值类型,把该值类型的值保存到栈中。
装箱是隐式的,拆箱是显示的。
因为 Object 是一切类型的基类,而装箱是值类型转换为 Object 类型的过程,也就是装箱是子类到父类的一个类型转换,所以装箱是隐式进行的,而拆箱是一个逆过程,从父类转换到子类,需要进行显示的转换(需要对类型强制转换)。
理清两个疑问:
值类型与引用类型的继承关系:值类型的基类是 System.ValueType,System.ValueType 的基类是 System.Object
为什么子类到父类是隐式、而父类到子类是显示:因为一个类只能有一个父类,但是可以有很多子类,所以从子类到父类是唯一的,父类到子类不是唯一的,需要显示指定转换的是哪一个子类。
耗费性能:相对于简单的赋值操作,装拆箱过程需要进行大量的计算,非常耗费性能
装箱:装箱过程需要在堆内存上分配内存空间,然后构造一个新的 Object 对象,该过程需要进行大量计算。
拆箱:拆箱过程需要进行强制转换,强制转换也需要进行大量的计算。
类型不安全:如果拆箱过程强制转换的类型与给定的类型不一致,会抛出 InvalidCastException (无效的强制转换)异常
int i = 123;
object o = i;
int j = (short)o;
需要尽量避免代码中出现装箱和拆箱的操作
在把值类型装箱之后,拆箱时需要确保接收的值类型与装箱之前的值类型一致,否则会出错