[C#] C# 知识回顾 - 装箱与拆箱

装箱与拆箱

目录

  • 生活中的装箱与拆箱
  • C# 的装箱与拆箱
  • 值类型和引用类型
  • 装箱
  • 拆箱
  • 读者看法

 

生活中的装箱与拆箱

     咱们习惯了在网上购物,此次你想买本编程书 -- 《C 语言从入门到放弃》 ,下单成功后,卖家会帮你将这本入坑指南打好包装,咱们能够称之为装箱;通过快递员的马不停蹄,风雨无阻,包裹就直接送到你手上了。你必定会以迅雷不及掩耳盗铃儿响叮当之势拆开包装,这个过程咱们能够称之为拆箱,这时,入坑指南就顺利的送到你手上。html

 

 

C# 的装箱与拆箱

  装箱:将值类型(如 int ,或自定义的值类型等)转换成 object 或者接口类型的一个过程。当 CLR 对值类型进行装箱时,会将该值包装为 System.Object 类型,再将包装后的对象存储在堆上。 拆箱就是从对象中提取对应的值类型的一个过程。

  装箱是隐式的;拆箱一定是显式的。 sql

  与简单的赋值操做相比,装箱和拆箱都须要进行大量的数据计算。对值类型进行装箱时,CLR 必须从新分配一个新的对象。拆箱所需的强制转换也须要进行大量的计算,二者相比,仅仅是程度不高,而且也可能会出现类型转换发生的异常情形。若是你的操做正处于循环的中心,经过测试(如:Stopwatch),你会很明显的感受到性能问题。编程

  .NET 2.0 引入的泛型其实在很大的程度上解决了装拆箱产生的类型转换问题,也减小了类型转换所引发的运行时的异常,及保证了类型安全,从而提升了性能。
        static void Main(string[] args)
        {
            var i = 123;    //System.Int32

            //对 i 装箱(隐式)
            object obj = i;

            //对 obj 进行拆箱(显式)
            i = (int)obj;

            Console.Read();
        }

  在这里,我先将变量 i (int 类型)进行了装箱,并分配给对象 obj。其次,再次将对象 obj 进行拆箱(即强转)并从新给变量 i(int 类型)赋值。数组

 

  直接经过反编译获得的 IL 代码,从 box 和 unbox 这两个指令也能够看出具体在哪一步发生装箱和拆箱操做。安全

 

值类型和引用类型

  值类型和引用类型,这二者原本没有多大的联系(可能就是基类为 object),设计人员经过一种名为装拆箱的操做使得这两种类型建立了新的联系,让任何值类型均可以当成对象(引用)类型来进行操做。框架

  装拆箱其实就是值类型和引用类型二者之间的类型转换操做。这里,我简单梳理一下这两种类型:ide

  (1)值类型:整型:Int;长整型:long;浮点型:float;字符型:char;布尔型:bool;枚举:enum;结构:struct;它们统一继承  System.ValueType。函数

  (2)引用类型:数组,用户定义的类、接口、委托,object,字符串等。post

  (3)简单的堆栈图:性能

图:@ 表示一个引用地址 

 

装箱

  装箱就是值类型到 object 类型或者到该值类型所实现的接口类型所实现的一个隐式转换过程(可显式)。装箱的时候会在堆中自动建立一个对象实例,而后将该值复制到新对象内。

    var i = 123;    //System.Int32

    //对 i 装箱(隐式)进对象 o
    object o = i;

  

  从图可知,对象 o 存的是地址引用,指向的是堆上的值,这个值的类型和变量 i 同样,也是 int 类型,值(123)也就是从变量 i Copy 过来的一个副本值而已。

  【备注】装箱默认是隐式的,固然,你能够选择显式,但这并非必须的。

 

拆箱

  拆箱是从 object 类型到值类型,或从接口类型到实现该接口的值类型的显式转换的一个过程。

  拆箱:检查对象实例,确保它是给定值类型的一个装箱值后,再将该值从实例复制到值类型变量中。

    int i = 123;      // 值类型
    object o = i;     // 装箱
    int j = (int)o;   // 拆箱

 

  要在运行时成功拆箱值类型,被拆箱的项必须是对一个对象的引用,该对象是先前经过装箱该值类型的实例建立的。

   

  拆箱时须要注意,转换出现异常的情形:

  虽然,decimal 类型能够直接强转为 int 类型,但从调式的结果来看,拆箱时是会引起“转换无效”的异常。要记住,拆箱时强转的值类型,应以装箱时的值类型一致。

 

读者看法

   深蓝医生:简单说,装箱就是把值类型变成引用类型使用;拆箱就是将引用类型变成值类型使用。然而,大量使用值类型会引发变量值的大量拷贝,反而下降运行效率。因此装箱没有那么可怕,这能够经过 EF的code first和SOD框架的code first代码进行测试(要有业务层代码这种),虽然SOD框架的实体类看起来都是“装箱”过的,可是它的性能不会输给EF。

   lulianqi15:最后加的一句注意(decimal 类型能够直接强转为 int 类型........应以装箱时的值类型一致),其实不太严谨,decimal 128位,想一想都不可能平白无故转换成32位的数据,之因此能强制转换,是由于Decimal 本身实现了自定义强制转换public static explicit operator int(decimal value)。回到最后例子的报错,JIT确定是知道obj是Decimal(由于Decimal数据移动到托管堆上后后还额外为其添加了类型对象指针及同步块索引,因此即便obj在ide里申明为object,不过jit是知道他就是Decimal)之因此发生异常的缘由是CLR认为在生成il时就认为obj是object类型,而object没有实现explicit 指定重载(固然能够本身实现)。因此就调用了object默认的强制转换,检查类型指针的时候发现不合法就报错了,那若是承认Decimal能够强制转换为int,说到底最后在强制转换报错的根本缘由也只是object没有实现explicit 指定重载。若是自定义类型本身实现了explicit,那在转换时也不用保证其运行时类型与要转换的类型一致。

 

 

 

 

「若是不想在世界上虚度一辈子,那就要学习一生。」-- 高尔基


【博主】反骨仔

【原文】http://www.cnblogs.com/liqingwen/p/6486332.html 

【参考】微软官方文档 MSDN

相关文章
相关标签/搜索