老生常谈--什么是装箱什么是拆箱

简介: 老生常谈--什么是装箱什么是拆箱

我们知道.NET具有两个数据类型:值类型和引用类型。因为值类型没有指针引用,不是分配在托管堆中,也不会被GC回收,因此它比引用类型更加高效。但有时我们需要将一种类型的变量转换为另一种类型,这时我们就可以使用装箱/拆箱。


一、什么是装箱

装箱就是将值类型的数据存储在引用类型的变量中。例如在方法中创建了int类型的变量,需要将这个值类型赋值给一个引用类型的变量,这就意味着对这个值进行了装箱操作,代码如下:

void demo()
{
  int num= 25;
  //这是装箱操作
  object objNum=num;
}

上面的代码就是将值类型分配给object类型变量的过程,这个就是装箱操作。当我们对一个值进行装箱时,CoreCLR会在堆上分配新对象,并将这个值类型的值复制到新分配的对象实例上,然后返回托管堆中新分配对象的引用。


二、什么是拆箱

将装箱反过来操作就是拆箱,也就是将引用类型变量的值转换回栈中值类型的过程。CoreCLR首先会验证接收的数据类型是否等同于被装类型,如果是就把值复制回基于栈存储的变量中。例如下面的代码中,objNum的底层类型是int,就完成了拆箱操作:

void UnBoxDemo()
{
  int num= 25;
  // 这是装箱操作
  object objNum= num;
  // 这是拆箱操作
  int num2 = (int)objNum;
}

Tip:与普通的类型转换不同,我们必须将其拆箱到一个恰当的数据类型中。如果我们将数据拆箱到不正确的数据类型中,会抛出InvalidCastException异常。因此为了安全起见,如果不能保证Object类型背后的类型,那么应该使用try/catch语句把拆箱操作包起来。


三、IL 代码

当编译器遇到装箱/拆箱语法时,它会生成包含装箱/拆箱操作的IL代码。使用ildasm.exe查看编译的程序集就会看到装箱和拆箱操作对应的box和unbox指令:

.method assembly hidebysig static
    void  '<<Main>$>g__UnBoxDemo|0_0'() cil managed
{
  .maxstack  1
  .locals init (int32 V_0, object V_1, int32 V_2)
    IL_0000:  nop
    IL_0001:  ldc.i4.s   25
    IL_0003:  stloc.0
    IL_0004:  ldloc.0
    IL_0005:  box        [System.Runtime]System.Int32
    IL_000a:  stloc.1
    IL_000b:  ldloc.1
    IL_000c:  unbox.any  [System.Runtime]System.Int32
    IL_0011:  stloc.2
    IL_0012:  ret
  } // end of method '<Program>$'::'<<Main>$>g__UnBoxDemo|0_0'

上面的IL代码中来看,装箱/拆箱似乎是一个没用的特性。因为我们很少需要在Object变量中存储值类型。但是实际是装箱/解箱过程是有用的,它允许假设一切都可以被当作Object类型来处理,CoreCLR会帮我们处理与内存有关的细节。


四、总结

从程序员角度来看装箱和拆箱是非常方便的,不需要手动去复制和转移内存中的值类型和引用类型的数据。但是装箱/拆箱背后的栈/堆内存转移也会带来性能问题。以下总结了简单的整型数进行装箱和拆箱的步骤:


  1. 在托管堆中分配新对象;
  2. 在栈中的数据值被转移到该托管堆中的对象上;
  3. 当拆箱时,存储在堆中对象上的值被转移回栈中;
  4. 堆上未使用的对象将最终被GC回收。


很多时候装箱和拆箱操作不会在性能方面造成重大影响,但是如果一个类似于ArrayList这样的集合包含成千上万条数据,而程序又会频繁操作这些数据,性能的影响就会很明显的。因此在平时编程时应尽量避免发生装箱/拆箱操作。


目录
相关文章
|
5月前
|
存储 测试技术 索引
在实际编程中避免过度使用自动装箱和拆箱
在实际编程中避免过度使用自动装箱和拆箱
|
缓存 Java 编译器
自动拆箱与装箱
自动拆箱与装箱
|
存储 Java 编译器
探寻Java装箱和拆箱的奥妙!
Java中的装箱和拆箱技术经历了从手动到自动、从普通到紧凑型的演进,这使得我们能够更方便地操作基本数据类型和封装类型之间的转换。
|
Java 程序员
Java包装类,基本的装箱与拆箱
将原始类型和包装类分开以保持简单。当需要一个适合像面向对象编程的类型时就需要包装类。当希望数据类型变得简单时就使用原始类型。
106 0
|
缓存 算法 Java
【JavaSE基础】包装类全解析以及使用(详解装箱与拆箱)
今天为大家讲解包装类的由来和使用知识,以及它的自动装箱和拆箱。文章将以常用Integer来讲解,同时会讲解Integer的特点。看完这篇保证你收获满满
140 0
【JavaSE基础】包装类全解析以及使用(详解装箱与拆箱)
谈谈JavaScript中装箱和拆箱
在JavaScript里面有个引用类型叫做基本包装类型,它包括String、Number和Boolean。那么它和基本的类型String、Number和Boolean是啥关系呢?接着往下看👀
谈谈JavaScript中装箱和拆箱
|
Java 编译器
详解JAVA包装类、自动拆箱和装箱
详解JAVA包装类、自动拆箱和装箱
146 0
详解JAVA包装类、自动拆箱和装箱
|
Java
Java面试题: 基础考核-拆箱装箱, 数据类型, MAP
Java面试题: 基础考核-拆箱装箱, 数据类型, MAP
104 0
|
编译器
减少装箱与拆箱
减少装箱与拆箱
137 0