3. 值类型的装箱与拆箱
①装箱过程?
装箱:将值类型转换为引用类型。当我们把值类型参数传递给需要引用类型参数的方法时,会自动进行装箱操作。过程如下:
从托管堆为要生成的引用类型分配大小。大小为:值类型实例本身的大小+额外空间(方法表指针和SyncBlockIndex)。
将值类型字段拷贝到刚刚分配的内存中。
返回托管堆中新分配内存的地址。也就是指向对象的引用。
②拆箱过程?
拆箱:获取指向对象中包含的值类型部分的指针。一般拆箱之后会进行字段拷贝操作,两个操作加起来才是真正与装箱互反的操作。过程如下:
如果引用为Null,则抛出NullReferenceException异常。
如果引用对象不是一个期望值类型的已装箱对象,会抛出InvalidCastException异常。
返回一个指向包含在已装箱对象中值类型部分的指针。
③实例
拆箱的转型结果必须是它原来未装箱时的类型。
public static void Main() {
Int32 x = 5;
Object o = x; // 装箱
Int16 y = (Int16) o; // 拆箱,抛出InvalidCastException异常
}
Int32 x = 5;
Object o = x; // 装箱
Int16 y = (Int16) o; // 拆箱,抛出InvalidCastException异常
}
修正:Int16 z=(Int16)(Int32)o;//拆箱成功
这段代码进行了几次装箱?
public static void Main() {
Int32 v = 5; // 创建值变量
Object o = v; // 装箱
v = 123; // Changes the unboxed value to 123
Console.WriteLine(v + ", " + (Int32) o); // Displays "123, 5" ,装箱两次
}
Int32 v = 5; // 创建值变量
Object o = v; // 装箱
v = 123; // Changes the unboxed value to 123
Console.WriteLine(v + ", " + (Int32) o); // Displays "123, 5" ,装箱两次
}
上面的代码进行了3次装箱,最后一行中v被装箱为引用类型,o首先被拆箱然后再装箱为引用类型。
我们看一下它的IL代码:
字符串连接实际上调用的是Concat方法,该方法的参数要求是object,因此各参数都要转换成object类型。
优化:
Console.WriteLine(v.ToString()+”,”+o);//装箱0次
我们要尽量少的进行值类型的装箱拆箱操作,以提高程序性能。