c#装箱与拆箱(Boxing&Unboxing)

128 阅读2分钟

本文已参与「新人创作礼亅活动,一起开启掘金创作之路。

1、装箱:值类型转换成引用类型

装箱时将发生:

  • 在托管堆分配内存,分配的内存大小是值类型各字段所需的内存大小之和,还要加上托管堆所有对象都有的两个额外成员(类型对象指针和同步块索引)所需的内存量
  • 值类型的字段复制到新分配的堆内存
  • 返回对象地址,该地址是对象引用:值类型成了引用类型
  • 该对象一直存在于堆内存中,直至被垃圾回收

2、拆箱:获取已装箱的堆内存上对象的各个字段地址,是一个获取指针地址的过程

拆箱时将发生:

  • 如果包含“对已装箱值类型实例引用”的变量为null,抛出NullReferenceException异常
  • 如果引用的对象不是所需值类型的已装箱实例,抛出InvalidCastException异常
  • 将该对象从实例复制到值类型中

截屏2022-06-23 23.55.42.png 图中 O指向 i 装箱(创建新对象)后在堆中的位置

装箱可以隐式执行,而拆箱需要显式执行

int i = 123;
System.Object o = i;   //隐式装箱  对应IL代码  box [System.Runtime]System.Int32
int j = (int)o;        //显示拆箱  对应IL代码  unbox.any [System.Runtime]System.Int32
int j = o;             //不是显示拆箱则报错:无法将类型“object”隐式转换为“int”。存在一个显式转换(是否缺少强制转换?)

//将变量直接写在输出语句时也会发生装箱
Console.WriteLine("The value-type value = {0}", i); //call void [System.Console]System.Console::WriteLine(string, object)
Console.WriteLine("The object-type value = {0}", o); 

装箱和取消装箱过程需要进行大量的计算,对值类型进行装箱时,必须创建一个全新的对象,应尽量避免

泛型

可使用Collections.Generic下的泛型方法、类、结构和接口,例如System.Collections.Generic.List而不使用System.Collections.ArrayList,因为后者存的对象是Object类型,而前者T是泛型,不会造成装箱或拆箱。泛型集合可指定存储的对象类型,能存储指定的对象或对象的子类,如果加入集合的类型错误则会报错。

using System.Collections.Generic;
public class Parent
{
}
public class Childen : Parent
{
}
public class Grandson : Childen
{
}
List<Childen> list = new List<Childen>();
list.Add(new Childen());    //编译通过
list.Add(new Parent());     //编译错误:无法从Parent转成Childen
list.Add(new Grandson());   //编译通过

泛型:泛型是为所存储或使用的一个或多个类型具有占位符的类、结构、接口、方法。泛型集合可以将类型形参用作其存储的对象类型的占位符。优点包括:代码的可重用性增加,类型安全性提高。