本文已参与「新人创作礼亅活动,一起开启掘金创作之路。
1、装箱:值类型转换成引用类型
装箱时将发生:
- 在托管堆分配内存,分配的内存大小是值类型各字段所需的内存大小之和,还要加上托管堆所有对象都有的两个额外成员(类型对象指针和同步块索引)所需的内存量
- 值类型的字段复制到新分配的堆内存
- 返回对象地址,该地址是对象引用:值类型成了引用类型
- 该对象一直存在于堆内存中,直至被垃圾回收
2、拆箱:获取已装箱的堆内存上对象的各个字段地址,是一个获取指针地址的过程
拆箱时将发生:
- 如果包含“对已装箱值类型实例引用”的变量为null,抛出NullReferenceException异常
- 如果引用的对象不是所需值类型的已装箱实例,抛出InvalidCastException异常
- 将该对象从实例复制到值类型中
图中 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()); //编译通过
泛型:泛型是为所存储或使用的一个或多个类型具有占位符的类、结构、接口、方法。泛型集合可以将类型形参用作其存储的对象类型的占位符。优点包括:代码的可重用性增加,类型安全性提高。