C#值类型的装箱与拆箱

值类型与引用类型的嵌套:

1。当引用类型中嵌套值类型时:

 值类型将作为引用类型实例的一部分被分配在托管堆中

2。当值类型嵌套引用类型时:

引用类型将做为值类型的成员变量,堆栈上保存该成员变量的内存地址,成员的实例将依然保存在托管堆中。

值类型 是比引用类型更为轻量级的类型,因为他没有被分配在托管堆中,不会被执行垃圾收集,也没有指向它们的指针。

装箱:就是将一个值类型数据转换为一个引用类型数据,装箱的操作步骤由以下组成:

1。从托管堆中为新生成的引用类型对象分配内存,分配的内存大小为,值类型实例本身大小加上其它额外的将该值类型实例视为真正的引用对象所需的空间,这些额外的空间包括一个方法表指针和一个SyncBlockIndex。

2。将值类型实例的字段拷贝到托管堆上新分配对象的内存中。

3。返回托管堆中新分配对象的地址,该地址就是一个指向对象的引用,值类型对象也就变成了一个引用类型对象

拆箱:就是将引用类型数据转换为值类型数据,拆箱的操作步骤由以下组成:

1。如果该引用为null,将会抛出一个 NullReferenceException异常。

2。如果该引用对象不是一个期望的值类型的已装箱对象,将会抛出一个InvalidCastException异常。

3。一个指向包含在已装箱对象中值类型部分的指针被返回,该指针指向的值类型对于引用类型对象所具有的附加成员(一个方法表指针和一个SyncBlockIndex)一无所知,实际上该指针指向的是已装箱对象中的未装箱部分。

通常拆箱操作后会紧跟着一个字段拷贝操作,将字段从托管堆拷贝到堆栈中。

装箱与拆箱并不是严格意义上的互反操作,拆箱的代价要比装箱操作小得多,装箱和拆箱都会从速度和内存两方面损伤应用程序的性能,所有应尽量减少这种情况的发生。

例子:

public static void Main()
{
Int32 v=5;
Object o=v;
v=123;
Console.WriteLine(v+","+(Int32)o);
}

在这段代码中共装箱3次:

1。Object o=v 处,

2。Console.WriteLine(v+","+(Int32)o); 处的v

3。Console.WriteLine(v+","+(Int32)o); 处的(Int32)o

后两处会产生装箱动作的原因是:Console.WriteLine(Object arg0,Object arg0,Object arg0);

例中的代码从性能上考虑可以优化为:Console.WriteLine(v.ToString()+","+o);

值类型数据没有方法表指针,所以未装箱的值类型实例来调用其上继承而来的虚方法;值类型数据没有SynBlockIndex所以不能利用System.Threading.Monitor类型来同步多个线程对它们的访问.

原文地址:https://www.cnblogs.com/challengesoflife/p/2332882.html