在理解装箱和拆箱之前,首先我们需要知道C#语言中有两大数据类型,分别是值类型和引用类型,值类型数据存放在栈上,引用类型数据则存放在堆上,而在栈上的是数据于堆上的初始地址。
数据存放的图解如下:
那么,为什么需要装箱和拆箱呢?从上图可以看出,值类型比引用类型更高效,因为它没有指针引用,不用分配在托管堆中,也不用被GC回收。但是有时候需要将一种类型的变量表示为另一种类型的变量,通过装箱和拆箱可以将数据在值类型和引用类型间进行转换。
1.装箱理解:将值类型的数据存储在一个引用类型的变量中。
int x = 100;
object boxInt = x;
当声明x时,在栈上分配一个4字节空间来存储x值,使用装箱操作 object boxInt = x 时,会将x的存储值复制到堆中,然后 boxInt 引用在堆中的值的地址。
2.拆箱理解:将object引用类型的数据转换回值类型数据的值。但是引用类型的数据的数据类型和要转换的值类型数据的数据类型要一致,否则将会抛出异常。
int x = 100;
object boxInt = x;//装箱
int y = (int)boxInt;//拆箱
当数据类型一致时,直接将boxInt的值复制回栈中的y的地址上。
3.实际应用:使用ArrayList来展示实际应用。
ArrayList类中有一个成员方法Add,定义如下:
public virtual int Add(object? value);
可以看到,方法中的参数类型是object,对Int32类型的值进行装箱,代码如下:
ArrayList myInt = new ArrayList();
int x = 12;
myInt.Add(x);
调用Add方法时,进行了隐式装箱,将Int32转换为object。
int y = (int)myInt[0];
Console.WriteLine(y);//输出12
将myInt中的数据拆箱复制到Int32类型的y变量。
4.总结:
装箱和拆箱不需要我们手动去复制和转移内存中的值。但是进行装箱和拆箱会造成程序的性能下降,应尽量避免进行装箱和拆箱。